@Component({ selector: 'my-app', template: '<h1>Ultra Racing</h1>'})
@Component : Used to apply our component decorator to our class.Decorators are a TypeScript feature.
selector :The CSS selector for the HTML element where we want the component to
load. The content we want to load inside our selector.
template
BrowserModule: Module needed for running Angular websites.
platformBrowserDynamic : Angular library that will render the website.
@Component({
selector: 'my-app'
template:'
{{title}}
' //interploation})
import {NgModule,Component} from '@angular/core'
import {BrowserModule} from '@angular/platform-broswer'
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
ngOnInit() {
this.carParts = CARPARTS;
} 1.5 Our First Component
index.html
<html> <head> <title>Racing App</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="styles.css"> <script src="https://unpkg.com/core-js/client/shim.min.js"></script> <script src="https://unpkg.com/zone.js@0.6.25?main=browser"></script> <script src="https://unpkg.com/reflect-metadata@0.1.8"></script> <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('app/main').catch(function(err){ console.error(err); }); </script> </head> <body> <racing-app> <h1>Loading...</h1></racing-app> </body> </html>
app/main.ts
import { NgModule, Component } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; @Component({ selector:'racing-app', template:`{{heading}}
` }) class AppComponent { heading = "Ultra Racing Schedule" } @NgModule({ imports: [ BrowserModule ], declarations: [AppComponent], bootstrap:[AppComponent] }) class AppModule {} platformBrowserDynamic() .bootstrapModule(AppModule);
______________________________________________________________________________
1.6 Making It Dynamic
app/main.ts
import { NgModule, Component } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; @Component({ selector: 'racing-app', template: `{{ heading }}
{{race.name}}
{{race.date}} {{race.about}} ` }) class AppComponent { heading = "Ultra Racing Schedule" race = { "id": 1, "name": "Daytona Thunderdome", "date": new Date('2512-01-04T14:00:00'), "about": "Race through the ruins of an ancient Florida battle arena.", "entryFee": 3200 } } @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent ], bootstrap: [ AppComponent ] }) class AppModule {} platformBrowserDynamic() .bootstrapModule(AppModule);
______________________________________________________________________________
2.4 Looping
2.5 Conditionals
______________________________________________________________________________
2.9 Methods
At the bottom of our template we've added a place to display our total cost. Our total cost will be calculated by summing up theentryFee
properties of ONLY the races we are racing in ( isRacing
istrue
). Write a method named totalCost()
in our AppComponent
that returns this value.app/main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule, Component } from '@angular/core'; @Component({ selector: 'racing-app', template: `{{heading}}
{{race.name}} {{race.entryFee | currency:'USD':true}}
{{race.date | date:'MMM d, y, h:MM a'}} {{race.about}}Already Racing
Total cost: {{totalCost() | currency:'USD':true}}
` }) export class AppComponent { heading = "Ultra Racing Schedule" cash = 10000; races = [{ "id": 1, "name": "Daytona Thunderdome", "date": new Date('2512-01-04T14:00:00'), "about": "Race through the ruins of an ancient Florida battle arena.", "entryFee": 3200, "isRacing": false }, { "id": 2, "name": "San Francisco Ruins", "date": new Date('2512-07-03T20:00:00'), "about": "Drift down the streets of a city almost sunk under the ocean.", "entryFee": 4700, "isRacing": true }, { "id": 3, "name": "New York City Skyline", "date": new Date('2512-07-12T21:00:00'), "about": "Fly between buildings in the electronic sky.", "entryFee": 4300, "isRacing": true }]; totalCost() { let sum = 0; for (let race of this.races){ if(race.isRacing) { sum += race.entryFee; } } return sum; } } @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent ], bootstrap: [ AppComponent ] }) class AppModule {} platformBrowserDynamic().bootstrapModule(AppModule);______________________________________________________________________________
3.3 Refactoring Classes
Both ourapp.component.ts
and our races.component.ts
are missing the export
keyword to allow their classes to be imported. Could you add them where they’re needed?app/app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'racing-app', template: `{{heading}}
my-races ` }) export class AppComponent { heading = "Ultra Racing Schedule" }
app/races.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'my-races', template: `Cash left to enter races: {{cashLeft() | currency:'USD':true}}
{{race.name}} {{race.entryFee | currency:'USD':true}}
{{race.date | date:'MMM d, y, h:MM a'}} {{race.about}}Already Racing
Total cost: {{totalCost() | currency:'USD':true}}
` }) export class RacesComponent { heading = "Ultra Racing Schedule" cash = 10000; races = [{ "id": 1, "name": "Daytona Thunderdome", "date": new Date('2512-01-04T14:00:00'), "about": "Race through the ruins of an ancient Florida battle arena.", "entryFee": 3200, "isRacing": false }, { "id": 2, "name": "San Francisco Ruins", "date": new Date('2512-07-03T20:00:00'), "about": "Drift down the streets of a city almost sunk under the ocean.", "entryFee": 4700, "isRacing": true }, { "id": 3, "name": "New York City Skyline", "date": new Date('2512-07-12T21:00:00'), "about": "Fly between buildings in the electronic sky.", "entryFee": 4300, "isRacing": true }]; totalCost() { let sum = 0; for (let race of this.races) { if (race.isRacing) sum += race.entryFee; } return sum; } cashLeft() { return this.cash - this.totalCost(); } }app/main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { RacesComponent } from './races.component'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent,RacesComponent ], bootstrap: [ AppComponent ] }) class AppModule {} platformBrowserDynamic().bootstrapModule(AppModule);
______________________________________________________________________________
3.5 Adding HTML and CSS
app/races.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'my-races', templateUrl:'app/races.component.html', styleUrls['app/races.component.css']; }) export class RacesComponent { heading = "Ultra Racing Schedule" cash = 10000; races = [{ "id": 1, "name": "Daytona Thunderdome", "date": new Date('2512-01-04T14:00:00'), "about": "Race through the ruins of an ancient Florida battle arena.", "entryFee": 3200, "isRacing": false }, { "id": 2, "name": "San Francisco Ruins", "date": new Date('2512-07-03T20:00:00'), "about": "Drift down the streets of a city almost sunk under the ocean.", "entryFee": 4700, "isRacing": true }, { "id": 3, "name": "New York City Skyline", "date": new Date('2512-07-12T21:00:00'), "about": "Fly between buildings in the electronic sky.", "entryFee": 4300, "isRacing": true }]; totalCost() { let sum = 0; for (let race of this.races) { if (race.isRacing) sum += race.entryFee; } return sum; } cashLeft() { return this.cash - this.totalCost(); } }
______________________________________________________________________________
DATA ORGANIZATION
3.8 Modeling and Mocking
app/race.ts
export class Race { id: number; name: string; date: Date; about: string; entryFee: number; isRacing: boolean; }
app/mocks.ts
import { Race } from './race'; export const RACES: races[]=[{ "id": 1, "name": "Daytona Thunderdome", "date": new Date('2512-01-04T14:00:00'), "about": "Race through the ruins of an ancient Florida battle arena.", "entryFee": 3200, "isRacing": false }, { "id": 2, "name": "San Francisco Ruins", "date": new Date('2512-07-03T20:00:00'), "about": "Drift down the streets of a city almost sunk under the ocean.", "entryFee": 4700, "isRacing": true }, { "id": 3, "name": "New York City Skyline", "date": new Date('2512-07-12T21:00:00'), "about": "Fly between buildings in the electronic sky.", "entryFee": 4300, "isRacing": true }];
app/races.component.ts
import { Component } from '@angular/core'; import { Race } from './race'; import { RACES } from './mocks'; @Component({ selector: 'my-races', templateUrl: 'app/races.component.html', styleUrls:['app/races.component.css'] }) export class RacesComponent { heading = "Ultra Racing Schedule" cash = 10000; races: Race[]; ngOnInit(){ this.races = RACES; } totalCost() { let sum = 0; for (let race of this.races) { if (race.isRacing) sum += race.entryFee; } return sum; } cashLeft() { return this.cash - this.totalCost(); } }
______________________________________________________________________________
DATA BINDING BOOST
4.1 Property & Class Binding
We've gone ahead and applied new HTML and CSS to our app to make it prettier. We also have two new fields in our model and mocks:
image
and imageDescription
.app/races.component.html
<main class="container" role="main"> <h2>Cash left to enter races: <span>{{cashLeft() | currency:'USD':true}}</span> </h2> <ul> <li class="card" *ngFor="let race of races" [class.racing]="race.isRacing" > <div class="panel-body"> <div class="photo"> <img [alt]="race.imageDescription" [src]= "race.image" > </div> <table class="race-info"> <tr> <td> <h3>{{race.name}}</h3> <p class="date">{{race.date | date:'MMM d, y, h:MM a'}}</p> <p class="description">{{race.about}}</p> </td> <td> <p class="price">{{race.entryFee | currency:'USD':true}}</p> </td> <td> <button class="button" *ngIf="!race.isRacing">Enter Race</button> <div *ngIf="race.isRacing"> <p class="status">Racing</p> <button class="button-cancel">Cancel Race</button> </div> </td> </tr> </table> </div> </li> </ul> <div class="price-total"> <h3>Total cost:</h3> <p>{{totalCost() | currency:'USD':true}}</p> </div> </main>
______________________________________________________________________________
Time to implement the "Enter Race" button and "Cancel Race" link.
app/races.component.html
<main class="container" role="main"> <h2>Cash left to enter races: <span>{{cashLeft() | currency:'USD':true}}</span> </h2> <ul> <li class="card" *ngFor="let race of races" [class.racing]="race.isRacing" > <div class="panel-body"> <div class="photo"> <img [src]="race.image" [alt]="race.imageDescription"> </div> <table class="race-info"> <tr> <td> <h3>{{race.name}}</h3> <p class="date">{{race.date | date:'MMM d, y, h:MM a'}}</p> <p class="description">{{race.about}}</p> </td> <td> <p class="price">{{race.entryFee | currency:'USD':true}}</p> </td> <td> <button class="button" *ngIf="!race.isRacing" (click)="enterRace(race)" >Enter Race</button> <div *ngIf="race.isRacing"> <p class="status">Racing</p> <button class="button-cancel" (click)="cancelRace(race)" >Cancel Race</button> </div> </td> </tr> </table> </div> </li> </ul> <div class="price-total"> <h3>Total cost:</h3> <p>{{totalCost() | currency:'USD':true}}</p> </div> </main>
app/races.component.ts
import { Component } from '@angular/core'; import { Race } from './race'; import { RACES } from './mocks'; @Component({ selector: 'my-races', templateUrl: 'app/races.component.html', styleUrls:['app/races.component.css'] }) export class RacesComponent { heading = "Ultra Racing Schedule" cash = 10000; races: Race[]; ngOnInit() { this.races = RACES; } totalCost() { let sum = 0; for (let race of this.races) { if (race.isRacing) sum += race.entryFee; } return sum; } cashLeft() { return this.cash - this.totalCost(); } cancelRace(race){ race.isRacing = false; } enterRace(race){ if(this.cashLeft()>race.entryFee){ race.isRacing = true; }else{ race.isRacing = false; alert( "You don't have enough cash"); } } }
______________________________________________________________________________
Sending Event Data
4.8 Input Binding
app/races.component.html
<main class="container" role="main"> <h2>Money for racing: <input type="text" class="cash" [(ngModel)]="cash"></h2> <h2>Cash left to enter races: <span>{{cashLeft() | currency:'USD':true}}</span> </h2> <ul> <li class="card" *ngFor="let race of races" [class.racing]="race.isRacing" > <div class="panel-body"> <div class="photo"> <img [src]="race.image" [alt]="race.imageDescription"> </div> <table class="race-info"> <tr> <td> <h3>{{race.name}}</h3> <p class="date">{{race.date | date:'MMM d, y, h:MM a'}}</p> <p class="description">{{race.about}}</p> </td> <td> <p class="price">{{race.entryFee | currency:'USD':true}}</p> </td> <td> <button class="button" *ngIf="!race.isRacing" (click)="enterRace(race)">Enter Race</button> <div *ngIf="race.isRacing"> <p class="status">Racing</p> <button class="button-cancel" (click)="cancelRace(race)">Cancel Race</button> </div> </td> </tr> </table> </div> </li> </ul> <div class="price-total"> <h3>Total cost:</h3> <p>{{totalCost() | currency:'USD':true}}</p> </div> </main>
This symbol
[()]
is sometimes called?banana in a box
______________________________________________________________________________
5.3 Moving to a Service 175 PTS
It's time to move our mocks into services for our racing schedule app. We've gone ahead and started creating a
race.service.ts
file for you. Please note that this is a RaceService
class, not RacingDataService
class like in the slides. We need your help hooking it all up.app/race.service.ts
import { RACES } from './mocks'; import { Injectable } from '@angular/core'; @Injectable() export class RaceService { getRaces() { return RACES; } }
app/main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { RacesComponent } from './races.component'; import { RaceService } from './race.service'; @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent ], providers: [ RaceService ], bootstrap: [ AppComponent ] }) class AppModule {} platformBrowserDynamic().bootstrapModule(AppModule);
app/races.component.ts
import { Component } from '@angular/core'; import { Race } from './race'; import { RaceService } from './race.service'; @Component({ selector: 'my-races', templateUrl: 'app/races.component.html', styleUrls:['app/races.component.css'] }) export class RacesComponent { heading = "Ultra Racing Schedule" cash = 10000; races: Race[]; constructor(private raceService: RaceService) { } ngOnInit() { this.races = this.raceService.getRaces(); } totalCost() { let sum = 0; for (let race of this.races) { if (race.isRacing) sum += race.entryFee; } return sum; } cashLeft() { return this.cash - this.totalCost(); } enterRace(race) { if (this.cashLeft() > race.entryFee) { race.isRacing = true; } else { alert("You don't have enough cash"); } } cancelRace(race) { race.isRacing = false; } }
______________________________________________________________________________
5.5 Over the Internet 250 PTS
We've gone ahead and created a
races.json
file for you — check it out. Let's import and usehttp
to fetch our data.______________________________________________________________________________
______________________________________________________________________________
______________________________________________________________________________
Exercise 1: MODULE
app.js
var app = angular.module('gemStore',[]);
index.html
<html ng-app ='gemStore'> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body> <h1>{{"Hello, Angular!"}}</h1> </body> </html>
Exercise 3: CONTROLLERS
app.js
(function(){ var gem = { name: 'Azurite', price: 2.95 }; var app = angular.module('gemStore', []); app.controller('StoreController',function(){ this.product = gem; }); })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <div class="product row"> <h3> {{store.product.name}} <em class="pull-right"> {{store.product.price}}</em> </h3> </div> </body> </html>
Exercise 4: NG-SHOW/NG-HIDE:
NOTICE: The value is in quotes
Index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body class="container" ng-controller="StoreController as store"> <div class="product row" ng-hide = "store.product.soldOut"> <h3> {{store.product.name}} <em class="pull-right">${{store.product.price}}</em> </h3> <button ng-show = "store.product.canPurchase">Add to Cart</button> </div> </body> </html>
Exercise 5: NG -REPEAT
app.js
(function() { var app = angular.module('gemStore', []); app.controller('StoreController', function(){ this.products = gems; }); var gems = [ { name: 'Azurite', price: 2.95 }, { name: 'Bloodstone', price: 5.95 }, { name: 'Zircon', price: 3.95 } ]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body class="container" ng-controller="StoreController as store"> <div class="product row" ng-repeat = "product in store.products"> <h3> {{product.name}} <em class="pull-right">${{product.price}}</em> </h3> </div> </body> </html>
Exercise 6: FILTERS
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body class="container" ng-controller="StoreController as store"> <div class="product row" ng-repeat="product in store.products"> <h3> {{product.name}} <em class="pull-right">{{product.price | currency }}</em> </h3> </div> </body> </html>
Exercise 7: FILTERS
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3> {{product.name}} <em class="pull-right">{{product.price | currency}}</em> </h3> <div class="gallery"> <img ng-src = "{{product.images[0]}}" /> </div> </div> </div> </body> </html>
Exercise 8: DISPLAYING THE FIRST IMAGE
ng-src
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body class="list-group" ng-controller="StoreController as store"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div class="gallery"> <div class="img-wrap"> <img ng-src="{{product.images[0]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat ="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> </div> </body> </html>
Exercise 9 : CREATING AND DISPLAYING TAB CONTROLLER
app.js
(function() { var app = angular.module('gemStore', []); app.controller('StoreController', function(){ this.products = gems; }); app.controller('TabController', function(){ this.tab = 1; this.setTab = function(ntab){ this.tab = ntab; }; this.isSet = function(ntab){ return this.tab == ntab; }; }); var gems = [ { name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org", createdOn: 1397490980837 }, { stars: 1, body: "This gem sucks.", author: "tim@example.org", createdOn: 1397490980837 }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif", ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org", createdOn: 1397490980837 }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org", createdOn: 1397490980837 }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-09.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org", createdOn: 1397490980837 }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org", createdOn: 1397490980837 }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org", createdOn: 1397490980837 }] } ]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body class="list-group" ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group-item" ng-repeat="product in store.products"> <h3> {{product.name}} <em class="pull-right">{{product.price | currency}}</em> </h3> <section ng-show="product.images.length"> <img ng-src="{{product.images[0]}}" /> <ul class="list-inline thumbs"> <li class="thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </section> <section class="tab" ng-controller="TabController as ctrl"> <ul class="nav nav-pills"> <li ng-class = "{active:ctrl.isSet(1)}"> <a href ng-click = "ctrl.setTab(1)">Description</a></li> <li ng-class = "{active:ctrl.isSet(2)}"> <a href ng-click = "ctrl.setTab(2)">Specs</a></li> <li ng-class = "{active:ctrl.isSet(3)}"> <a href ng-click = "ctrl.setTab(3)">Reviews</a></li> </ul> <div ng-show = "ctrl.isSet(1)"> <h4>Description</h4> <blockquote>{{product.description}}</blockquote> </div> <div ng-show = "ctrl.isSet(2)"> <h4>Specs</h4> <blockquote>Shine:{{product.shine}} </blockquote> </div> <div ng-show = "ctrl.isSet(3)"> <h4>Reviews</h4> <blockquote></blockquote> </div> </section> </div> </body> </html>
Exercise 10 : CREATE & USING GALLERY CONTROLLER
app.js
(function() { var app = angular.module('gemStore', []); app.controller('GalleryController',function(){ this.current = 0; this.setCurrent = function(value) { this.current = value || 0; }; }); app.controller('StoreController', function(){ this.products = gems; }); app.controller('TabController', function(){ this.tab = 1; this.setTab = function(newValue){ this.tab = newValue; }; this.isSet = function(tabName){ return this.tab === tabName; }; }); var gems = [ { name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org", createdOn: 1397490980837 }, { stars: 1, body: "This gem sucks.", author: "tim@example.org", createdOn: 1397490980837 }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif", ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org", createdOn: 1397490980837 }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org", createdOn: 1397490980837 }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-09.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org", createdOn: 1397490980837 }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org", createdOn: 1397490980837 }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org", createdOn: 1397490980837 }] } ]; })();
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body class="list-group" ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group-item" ng-repeat="product in store.products"> <h3> {{product.name}} <em class="pull-right">{{product.price | currency}}</em> </h3> <div class='gallery' ng-show="product.images.length" ng-controller="GalleryController as gallery"> <img ng-src="{{product.images[gallery.current]}}" /> <ul class="list-inline thumbs"> <li class="thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section class="tab" ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active: tab.isSet(1) }"> <a href ng-click="tab.setTab(1)">Description</a></li> <li ng-class="{ active: tab.isSet(2) }"> <a href ng-click="tab.setTab(2)">Specs</a></li> <li ng-class="{ active: tab.isSet(3) }"> <a href ng-click="tab.setTab(3)">Reviews</a></li> </ul> <div ng-show="tab.isSet(1)"> <h4>Description</h4> <blockquote>{{product.description}}</blockquote> </div> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <blockquote>Shine: {{product.shine}}</blockquote> </div> <div ng-show="tab.isSet(3)"> <h4>Reviews</h4> </div> </section> </div> </body> </html>
Exercise 10 : GALLERY CONTROLLER
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href="" ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href="" ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href="" ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)"> <h4>Description</h4> <blockquote>{{product.description}}</blockquote> </div> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <blockquote>Shine: {{product.shine}}</blockquote> </div> <div ng-show="tab.isSet(3)"> <ul> <h4>Reviews</h4> <li ng-repeat="review in product.reviews"> <blockquote> <strong>{{review.stars}} Stars</strong> {{review.body}} <cite class="clearfix">-{{review.author}}</cite> </blockquote> </li> </ul> <form name="reviewForm"> <blockquote> {{review.body}} <strong>{{review.stars}} Stars</strong> <cite class="clearfix">-{{review.author}}</cite> </blockquote> <h4>Submit a Review</h4> <fieldset class="form-group"> <select ng-model="review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars"> <option value="">Rate the Product</option> </select> </fieldset> <fieldset class="form-group"> <textarea ng-model="review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea> </fieldset> <fieldset class="form-group"> <input ng-model="review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" /> </fieldset> <fieldset class="form-group"> <input type="submit" class="btn btn-primary pull-right" value="Submit Review" /> </fieldset> </form> </div> </section> </div> </div> </body> </html>
Exercise 11 : CREATE & USING REVIEW CONTROLLER
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href="" ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href="" ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href="" ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)"> <h4>Description</h4> <blockquote>{{product.description}}</blockquote> </div> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <blockquote>Shine: {{product.shine}}</blockquote> </div> <div ng-show="tab.isSet(3)"> <ul> <h4>Reviews</h4> <li ng-repeat="review in product.reviews"> <blockquote> <strong>{{review.stars}} Stars</strong> {{review.body}} <cite class="clearfix">—{{review.author}}</cite> </blockquote> </li> </ul> <form name="reviewForm"> <blockquote> <strong> Stars</strong> <cite class="clearfix">—</cite> </blockquote> <h4>Submit a Review</h4> <fieldset class="form-group"> <select ng-model ="review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars"> <option value="">Rate the Product</option> </select> </fieldset> <fieldset class="form-group"> <textarea ng-model ="review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea> </fieldset> <fieldset class="form-group"> <input ng-model ="review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" /> </fieldset> <fieldset class="form-group"> <input type="submit" class="btn btn-primary pull-right" value="Submit Review" /> </fieldset> </form> </div> </section> </div> </div> </body> </html>
app.js
(function() { var app = angular.module('gemStore', []); app.controller('StoreController', function(){ this.products = gems; }); app.controller('TabController', function(){ this.tab = 1; this.setTab = function(tab){ this.tab = tab; }; this.isSet = function(tab){ return (this.tab === tab); }; }); app.controller('GalleryController', function(){ this.current = 0; this.setCurrent = function(index){ this.current = index; }; }); app.controller('ReviewController',function(){ this.review = {}; this.addReview = function(product){ product.reviews.push(this.review); this.review = {}; }; }); var gems = [ { name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org", createdOn: 1397490980837 }, { stars: 1, body: "This gem sucks.", author: "tim@example.org", createdOn: 1397490980837 }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif", ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org", createdOn: 1397490980837 }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org", createdOn: 1397490980837 }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-08.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org", createdOn: 1397490980837 }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org", createdOn: 1397490980837 }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org", createdOn: 1397490980837 }] } ]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href="" ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href="" ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href="" ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)"> <h4>Description</h4> <blockquote>{{product.description}}</blockquote> </div> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <blockquote>Shine: {{product.shine}}</blockquote> </div> <div ng-show="tab.isSet(3)"> <ul> <h4>Reviews</h4> <li ng-repeat="review in product.reviews"> <blockquote> <strong>{{review.stars}} Stars</strong> {{review.body}} <cite class="clearfix">—{{review.author}}</cite> </blockquote> </li> </ul> <form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewCtrl.addReview(product)" > <blockquote> <strong>{{reviewCtrl.review.stars}} Stars</strong> {{reviewCtrl.review.body}} <cite class="clearfix">—{{reviewCtrl.review.author}}</cite> </blockquote> <h4>Submit a Review</h4> <fieldset class="form-group"> <select ng-model="reviewCtrl.review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars"> <option value="">Rate the Product</option> </select> </fieldset> <fieldset class="form-group"> <textarea ng-model="reviewCtrl.review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea> </fieldset> <fieldset class="form-group"> <input ng-model="reviewCtrl.review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" /> </fieldset> <fieldset class="form-group"> <input type="submit" class="btn btn-primary pull-right" value="Submit Review" /> </fieldset> </form> </div> </section> </div> </div> </body> </html>
FORM VALIDATION
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href="" ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href="" ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href="" ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)"> <h4>Description</h4> <blockquote>{{product.description}}</blockquote> </div> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <blockquote>Shine: {{product.shine}}</blockquote> </div> <div ng-show="tab.isSet(3)"> <ul> <h4>Reviews</h4> <li ng-repeat="review in product.reviews"> <blockquote> <strong>{{review.stars}} Stars</strong> {{review.body}} <cite class="clearfix">—{{review.author}}</cite> </blockquote> </li> </ul> <form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewForm.$valid && reviewCtrl.addReview(product)" novalidate> <blockquote > <strong>{{reviewCtrl.review.stars}} Stars</strong> {{reviewCtrl.review.body}} <cite class="clearfix">—{{reviewCtrl.review.author}}</cite> </blockquote> <h4>Submit a Review</h4> <fieldset class="form-group"> <select ng-model="reviewCtrl.review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars" required> <option value="">Rate the Product</option> </select> </fieldset> <fieldset class="form-group"> <textarea ng-model="reviewCtrl.review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea> </fieldset> <fieldset class="form-group"> <input ng-model="reviewCtrl.review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" required/> </fieldset> <fieldset class="form-group"> <input type="submit" class="btn btn-primary pull-right" value="Submit Review" /> </fieldset> </form> </div> </section> </div> </div> </body> </html>
FORM STYLING
application.css
.ng-invalid.ng-dirty{ border-color:red } .ng-valid.ng-dirty{ border-color:green }
app.js
(function() { var app = angular.module('gemStore', []); app.controller('StoreController', function() { this.products = gems; }); app.controller("TabController", function() { this.tab = 1; this.isSet = function(checkTab) { return this.tab === checkTab; }; this.setTab = function(setTab) { this.tab = setTab; }; }); app.controller('GalleryController', function(){ this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }); app.controller("ReviewController", function(){ this.review = {}; this.addReview = function(product){ this.review.createdOn = Date.now(); product.reviews.push(this.review); this.review = {}; }; }); var gems = [{ name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org", createdOn: 1397490980837 }, { stars: 1, body: "This gem sucks.", author: "tim@example.org", createdOn: 1397490980837 }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif", ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org", createdOn: 1397490980837 }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org", createdOn: 1397490980837 }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-08.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org", createdOn: 1397490980837 }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org", createdOn: 1397490980837 }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org", createdOn: 1397490980837 }] }]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)"> <h4>Description</h4> <blockquote>{{product.description}}</blockquote> </div> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <blockquote>Shine: {{product.shine}}</blockquote> </div> <div ng-show="tab.isSet(3)"> <ul> <h4>Reviews</h4> <li ng-repeat="review in product.reviews"> <blockquote> <strong>{{review.stars}} Stars</strong> {{review.body}} <cite class="clearfix">—{{review.author}}+"on"+{{review.createdOn |date}}</cite> </blockquote> </li> </ul> <form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewCtrl.addReview(product)"> <blockquote > <strong>{{reviewCtrl.review.stars}} Stars</strong> {{reviewCtrl.review.body}} <cite class="clearfix">—{{reviewCtrl.review.author}}</cite> </blockquote> <h4>Submit a Review</h4> <fieldset class="form-group"> <select ng-model="reviewCtrl.review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars"> <option value>Rate the Product</option> </select> </fieldset> <fieldset class="form-group"> <textarea ng-model="reviewCtrl.review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea> </fieldset> <fieldset class="form-group"> <input ng-model="reviewCtrl.review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" /> </fieldset> <fieldset class="form-group"> <input type="submit" class="btn btn-primary pull-right" value="Submit Review" /> </fieldset> </form> </div> </section> </div> </div> </body> </html>
Exercise 12 : REFACTORING DESCRIPTION TAB
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)" ng-include = "'product-description.html'"></div> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <ul class="list-unstyled"> <li> <strong>Shine</strong> : {{product.shine}}</li> <li> <strong>Faces</strong> : {{product.faces}}</li> <li> <strong>Rarity</strong> : {{product.rarity}}</li> <li> <strong>Color</strong> : {{product.color}}</li> </ul> </div> <div ng-show="tab.isSet(3)"> <ul> <h4>Reviews</h4> <li ng-repeat="review in product.reviews"> <blockquote> <strong>{{review.stars}} Stars</strong> {{review.body}} <cite class="clearfix">—{{review.author}}</cite> </blockquote> </li> </ul> <form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewCtrl.addReview(product)"> <blockquote > <strong>{{reviewCtrl.review.stars}} Stars</strong> {{reviewCtrl.review.body}} <cite class="clearfix">—{{reviewCtrl.review.author}}</cite> </blockquote> <h4>Submit a Review</h4> <fieldset class="form-group"> <select ng-model="reviewCtrl.review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars"> <option value>Rate the Product</option> </select> </fieldset> <fieldset class="form-group"> <textarea ng-model="reviewCtrl.review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea> </fieldset> <fieldset class="form-group"> <input ng-model="reviewCtrl.review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" /> </fieldset> <fieldset class="form-group"> <input type="submit" class="btn btn-primary pull-right" value="Submit Review" /> </fieldset> </form> </div> </section> </div> </div> </body> </html>
product-description.html
<h4>Description</h4> <blockquote>{{product.description}}</blockquote>
CREATING AN ELEMENT DIRECTIVE
4.3 Creating an Element Directive
app.js
(function() { var app = angular.module('gemStore', []); app.controller('GalleryController', function(){ this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }); app.controller('StoreController', function() { this.products = gems; }); app.controller("TabController", function() { this.tab = 1; this.isSet = function(checkTab) { return this.tab === checkTab; }; this.setTab = function(setTab) { this.tab = setTab; }; }); app.controller("ReviewController", function(){ this.review = {}; this.addReview = function(product){ product.reviews.push(this.review); this.review = {}; }; }); app.directive("productDescription",function(){ return { restrict:'E', templateUrl:"product-description.html" }; }); var gems = [ { name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org" }, { stars: 1, body: "This gem sucks.", author: "tim@example.org" }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif" ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org" }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org" }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-08.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org" }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org" }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org" }] } ]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <product-description ng-show="tab.isSet(1)" ></product-description> <div ng-show="tab.isSet(2)"> <h4>Specs</h4> <ul class="list-unstyled"> <li> <strong>Shine</strong> : {{product.shine}}</li> <li> <strong>Faces</strong> : {{product.faces}}</li> <li> <strong>Rarity</strong> : {{product.rarity}}</li> <li> <strong>Color</strong> : {{product.color}}</li> </ul> </div> <div ng-show="tab.isSet(3)"> <ul> <h4>Reviews</h4> <li ng-repeat="review in product.reviews"> <blockquote> <strong>{{review.stars}} Stars</strong> {{review.body}} <cite class="clearfix">—{{review.author}}</cite> </blockquote> </li> </ul> <form name="reviewForm" ng-controller="ReviewController as reviewCtrl" ng-submit="reviewCtrl.addReview(product)"> <blockquote > <strong>{{reviewCtrl.review.stars}} Stars</strong> {{reviewCtrl.review.body}} <cite class="clearfix">—{{reviewCtrl.review.author}}</cite> </blockquote> <h4>Submit a Review</h4> <fieldset class="form-group"> <select ng-model="reviewCtrl.review.stars" class="form-control" ng-options="stars for stars in [5,4,3,2,1]" title="Stars"> <option value>Rate the Product</option> </select> </fieldset> <fieldset class="form-group"> <textarea ng-model="reviewCtrl.review.body" class="form-control" placeholder="Write a short review of the product..." title="Review"></textarea> </fieldset> <fieldset class="form-group"> <input ng-model="reviewCtrl.review.author" type="email" class="form-control" placeholder="jimmyDean@example.org" title="Email" /> </fieldset> <fieldset class="form-group"> <input type="submit" class="btn btn-primary pull-right" value="Submit Review" /> </fieldset> </form> </div> </section> </div> </div> </body> </html>
4.4 Creating an Attribute Directive
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <section ng-controller="TabController as tab"> <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)" ng-include="product-description.html"></div> <div ng-show="tab.isSet(2)" product-specs> </div> <product-reviews ng-show="tab.isSet(3)"></product-reviews> </section> </div> </div> </body> </html>
product-specs.html
<h4>Specs</h4> <ul class="list-unstyled"> <li> <strong>Shine</strong> : {{product.shine}}</li> <li> <strong>Faces</strong> : {{product.faces}}</li> <li> <strong>Rarity</strong> : {{product.rarity}}</li> <li> <strong>Color</strong> : {{product.color}}</li> </ul>
app.js
(function() { var app = angular.module('gemStore', []); app.controller('GalleryController', function(){ this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }); app.controller('StoreController', function() { this.products = gems; }); app.controller("TabController", function() { this.tab = 1; this.isSet = function(checkTab) { return this.tab === checkTab; }; this.setTab = function(setTab) { this.tab = setTab; }; }); app.controller("ReviewController", function(){ this.review = {}; this.addReview = function(product){ product.reviews.push(this.review); this.review = {}; }; }); app.directive("productDescriptions", function() { return { restrict: 'E', templateUrl: "product-description.html" }; }); app.directive("productReviews", function() { return { restrict: 'E', templateUrl: "product-reviews.html" }; }); app.directive("productSpecs",function(){ return { restrict:'A', templateUrl:"product-specs.html" }; }); var gems = [ { name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org" }, { stars: 1, body: "This gem sucks.", author: "tim@example.org" }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif" ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org" }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org" }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-08.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org" }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org" }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org" }] } ]; })();
_________________________________________________________________________________
DIRECTIVE CONTROLLERS
DC :EXERCISE 1
app.js
(function() { var app = angular.module('gemStore', []); app.controller('GalleryController', function(){ this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }); app.controller('StoreController', function() { this.products = gems; }); app.directive("productTabs",function(){ return{ restrict:'E', templateUrl:'product-tabs.html', controller: function() { this.tab = 1; this.isSet = function(checkTab) { return this.tab === checkTab; }; this.setTab = function(setTab) { this.tab = setTab; }; }, controllerAs:'tab' }; }); app.controller("ReviewController", function(){ this.review = {}; this.addReview = function(product){ product.reviews.push(this.review); this.review = {}; }; }); app.directive("productDescription", function() { return { restrict: 'E', templateUrl: "product-description.html" }; }); app.directive("productReviews", function() { return { restrict: 'E', templateUrl: "product-reviews.html" }; }); app.directive("productSpecs", function() { return { restrict:"A", templateUrl: "product-specs.html" }; }); var gems = [ { name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org" }, { stars: 1, body: "This gem sucks.", author: "tim@example.org" }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif" ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org" }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org" }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-08.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org" }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org" }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org" }] } ]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <div ng-controller="GalleryController as gallery" ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div> <product-tabs></product-tabs> </div> </div> </body> </html>
product-tabs.html
<section > <ul class="nav nav-pills"> <li ng-class="{ active:tab.isSet(1) }"> <a href ng-click="tab.setTab(1)">Description</a> </li> <li ng-class="{ active:tab.isSet(2) }"> <a href ng-click="tab.setTab(2)">Specs</a> </li> <li ng-class="{ active:tab.isSet(3) }"> <a href ng-click="tab.setTab(3)">Reviews</a> </li> </ul> <div ng-show="tab.isSet(1)" ng-include="'product-description.html'"> </div> <div product-specs ng-show="tab.isSet(2)"></div> <product-reviews ng-show="tab.isSet(3)"></product-reviews> </section>
_______________________________________________________________________________
4.7 Refactoring Product Gallery
app.js
(function() { var app = angular.module('gemStore', []); app.directive('productGallery',function(){ return{ restrict:'E', templateUrl:'product-gallery.html', controller: function(){ this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }, controllerAs : 'gallery' }; }); app.controller('StoreController', function() { this.products = gems; }); app.controller("ReviewController", function(){ this.review = {}; this.addReview = function(product){ product.reviews.push(this.review); this.review = {}; }; }); app.directive("productDescriptions", function() { return { restrict: 'E', templateUrl: "product-description.html" }; }); app.directive("productReviews", function() { return { restrict: 'E', templateUrl: "product-reviews.html" }; }); app.directive("productSpecs", function() { return { restrict:"A", templateUrl: "product-specs.html" }; }); app.directive("productTabs", function() { return { restrict: "E", templateUrl: "product-tabs.html", controller: function() { this.tab = 1; this.isSet = function(checkTab) { return this.tab === checkTab; }; this.setTab = function(activeTab) { this.tab = activeTab; }; }, controllerAs: "tab" }; }); var gems = [ { name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [{ stars: 5, body: "I love this gem!", author: "joe@example.org" }, { stars: 1, body: "This gem sucks.", author: "tim@example.org" }] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif" ], reviews: [{ stars: 3, body: "I think this gem was just OK, could honestly use more shine, IMO.", author: "JimmyDean@example.org" }, { stars: 4, body: "Any gem with 12 faces is for me!", author: "gemsRock@example.org" }] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-08.gif" ], reviews: [{ stars: 1, body: "This gem is WAY too expensive for its rarity value.", author: "turtleguyy@example.org" }, { stars: 1, body: "BBW: High Shine != High Quality.", author: "LouisW407@example.org" }, { stars: 1, body: "Don't waste your rubles!", author: "nat@example.org" }] } ]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <product-gallery></product-gallery> <product-tabs></product-tabs> </div> </div> </body> </html>
product-gallery.html
<div ng-show="product.images.length"> <div class="img-wrap"> <img ng-src="{{product.images[gallery.current]}}" /> </div> <ul class="img-thumbnails clearfix"> <li class="small-image pull-left thumbnail" ng-repeat="image in product.images"> <img ng-src="{{image}}" /> </li> </ul> </div>
_______________________________________________________________________________
_______________________________________________________________________________
5.2 Refactoring into a Module
products.js
(function() { var app = angular.module("store-directives",[]); app.directive("productDescription", function() { return { restrict: 'E', templateUrl: "product-description.html" }; }); app.directive("productReviews", function() { return { restrict: 'E', templateUrl: "product-reviews.html" }; }); app.directive("productSpecs", function() { return { restrict:"A", templateUrl: "product-specs.html" }; }); app.directive("productTabs", function() { return { restrict: "E", templateUrl: "product-tabs.html", controller: function() { this.tab = 1; this.isSet = function(checkTab) { return this.tab === checkTab; }; this.setTab = function(activeTab) { this.tab = activeTab; }; }, controllerAs: "tab" }; }); app.directive("productGallery", function() { return { restrict: "E", templateUrl: "product-gallery.html", controller: function() { this.current = 0; this.setCurrent = function(imageNumber){ this.current = imageNumber || 0; }; }, controllerAs: "gallery" }; }); })();
app.js
(function() { var app = angular.module('gemStore', ['store-directives']); app.controller('StoreController', function(){ this.products = gems; }); app.controller('ReviewController', function() { this.review = {}; this.addReview = function(product) { product.reviews.push(this.review); this.review = {}; }; }); var gems = [{ name: 'Azurite', description: "Some gems have hidden qualities beyond their luster, beyond their shine... Azurite is one of those gems.", shine: 8, price: 110.50, rarity: 7, color: '#CCC', faces: 14, images: [ "images/gem-02.gif", "images/gem-05.gif", "images/gem-09.gif" ], reviews: [] }, { name: 'Bloodstone', description: "Origin of the Bloodstone is unknown, hence its low value. It has a very high shine and 12 sides, however.", shine: 9, price: 22.90, rarity: 6, color: '#EEE', faces: 12, images: [ "images/gem-01.gif", "images/gem-03.gif", "images/gem-04.gif", ], reviews: [] }, { name: 'Zircon', description: "Zircon is our most coveted and sought after gem. You will pay much to be the proud owner of this gorgeous and high shine gem.", shine: 70, price: 1100, rarity: 2, color: '#000', faces: 6, images: [ "images/gem-06.gif", "images/gem-07.gif", "images/gem-08.gif" ], reviews: [] }]; })();
index.html
<html ng-app="gemStore"> <head> <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> <script type="text/javascript" src="angular.min.js"></script> <script type="text/javascript" src="app.js"></script> <script type="text/javascript" src="products.js"></script> </head> <body ng-controller="StoreController as store"> <header> <h1 class="text-center">Flatlander Crafted Gems</h1> <h2 class="text-center">– an Angular store –</h2> </header> <div class="list-group"> <div class="list-group-item" ng-repeat="product in store.products"> <h3>{{product.name}} <em class="pull-right">{{product.price | currency}}</em></h3> <product-gallery></product-gallery> <product-tabs></product-tabs> </div> </div> </body> </html>
_______________________________________________________________________________
5.4 Built-in AngularJS Services
app.js
(function() { var app = angular.module('gemStore', ['store-directives']); app.controller('StoreController',['$http',function($http){ var store = this; store.products = []; $http.get('/store-products.json').success(function(data){ store.products = data; }); }]); app.controller('ReviewController', function() { this.review = {}; this.addReview = function(product) { product.reviews.push(this.review); this.review = {}; }; }); })();
______________________________________________________
1.2 The Beginning of All Routes
js/app.js
angular.module('NoteWrangler',['ngRoute']) .config(['$routeProvider',function($routeProvider){ $routeProvider.when('/notes',{ templateUrl:'templates/pages/notes/index.html' }); }]);
index.html
<html lang="en" ng-app="NoteWrangler"> <head> <meta charset="utf-8"> <title>Note Wrangler</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/application.css" /> </head> <body> <div class="nav-wrapper has-dropdown"> <div class="nav-content"> <div class="wrapper"> <div class="nav-content-layout"> <div class="nav-list"> <a href="#/" class="list-item" ng-click="selected = 'notes'" ng-class="{'active': selected == 'notes'}"> Notes </a> <a href="#/users" class="list-item" ng-click="selected = 'users'" ng-class="{'active': selected == 'users'}"> Users </a> </div> </div> </div> </div> </div> <div class="hero-wrapper"> <div class="hero-content"> <div class="hero"></div> </div> </div> <div class="main-wrapper"> <div ng-view></div> </div> <script src="./js/vendor/jquery.js"></script> <script src="./js/vendor/angular.js"></script> <script src="./js/vendor/angular-route.js"></script> <script src="./js/app.js"></script> <script src="./js/controllers/notes-create-controller.js"></script> <script src="./js/controllers/notes-edit-controller.js"></script> <script src="./js/controllers/notes-index-controller.js"></script> <script src="./js/controllers/notes-show-controller.js"></script> <script src="./js/controllers/users-index-controller.js"></script> <script src="./js/controllers/users-show-controller.js"></script> </body> </html>
1.4 A New Route and a Root Route
js/app.js
angular.module('NoteWrangler', ['ngRoute']) .config(['$routeProvider', function($routeProvider) { $routeProvider .when('/notes/new',{ templateUrl:'templates/pages/notes/edit.html' }).when('/',{ redirectTo:'/users' }) .when('/notes', { templateUrl: 'templates/pages/notes/index.html' }) .when('/users', { templateUrl: 'templates/pages/users/index.html' }).otherwise({ redirectTo:'/notes' }) ; }]);
_________________________________________________________________________________
llama.js
var newLlamaRecruit = { llamaOne: 'Larry Llama' }; $http({ method:'POST', url:'/llamas.json', data: newLlamaRecruit });
1.9 Inside Route Controller
js/app.js
angular.module('NoteWrangler', ['ngRoute']) .config(['$routeProvider', function($routeProvider) { $routeProvider .when('/', { redirectTo: '/users' }) .when('/notes', { templateUrl: 'templates/pages/notes/index.html', controller: function($http) { var donkey = this; // closes over 'this' $http({method: 'GET', url: '/notes'}).success(function(data) { donkey.notes = data; }); }, controllerAs: 'notesCtrl' }) .when('/users', { templateUrl: 'templates/pages/users/index.html' }) .when('/notes/new', { templateUrl: 'templates/pages/notes/edit.html' }) .otherwise({ redirectTo: '/notes' }); }]);
Notes
_________________________________________________________________________________
1.10 Outside Controller
app/js/app.js
angular.module('NoteWrangler', ['ngRoute']) .config(['$routeProvider', function($routeProvider) { $routeProvider .when('/', { redirectTo: '/users' }) .when('/notes', { templateUrl: 'templates/pages/notes/index.html', controller: function($http){ var controller = this; $http({method: 'GET', url: '/notes'}).success(function(data){ controller.notes = data; }); }, controllerAs: 'notesCtrl' }) .when('/users', { templateUrl: 'templates/pages/users/index.html' }) .when('/notes/new', { templateUrl: 'templates/pages/notes/edit.html', controller :'NotesCreateController', controllerAs:'note' }) .otherwise({ redirectTo: '/notes' }); }]);
app/templates/pages/notes/new.html
<div class="new-note"> <div class="new-note-container"> <form class="form"> <fieldset> <label for="title">Title</label> <input name="title" ng-model ="note.title"/> </fieldset> <fieldset> <label for="description">Description</label> <textarea name="description" ng-model="note.description"></textarea> </fieldset> <fieldset> <label for="content">Content</label> <textarea name="content" ng-model ="note.content" ></textarea> </fieldset> <button class="btn" ng-click= "note.saveNote(note)">Save</button> </form> </div> </div>
_________________________________________________________________________________
app/js/notes-show-controller.js
angular.module('NoteWrangler') .controller('NotesShowController', ['$http','$routeParams',function($http,$routeParams) { var notes = this; $http({method:'GET', url: '/notes/' + $routeParams.id}).success(function(data){ notes.note = data; }); }]);
_________________________________________________________________________________
2.3 Inline Controller & $scope
js/directives/nw-card.js
angular.module('NoteWrangler').directive('nwCard', [function() { return { restrict: 'E', templateUrl: 'templates/directives/nw-card.html', controller: function($scope) { $scope.header = 'Note Title'; $scope.description = 'A lovely note description.'; } }; }]);
templates/directives/nw-card.html
<div class="card" title="{{header}}"> <h2 class="h3">{{header}}</h2> <p>{{description}}</p> </div>
_________________________________________________________________________________
2.7 Setting on Scope
angular.module('NoteWrangler', []) .directive("nwCard", function() { return { restrict: "E", templateUrl: "templates/directives/nw-card.html", controller: function($scope) { $scope.title = 'Space Llamas'; } } });
_________________________________________________________________________________
2.8 A Flexible Card Directive
nw-card.js
angular.module('NoteWrangler') .directive('nwCard', function() { return { restrict: 'E', templateUrl: 'templates/directives/nw-card.html', scope:{ header:"=", description :"=" } }; });
nw-card.html
<div class="card" title="{{header}}"> <h2 class="h3">{{header}}</h2> <p>{{description}}</p> </div>
_________________________________________________________________________________
2.9 Making Isolation Work for You
notes/index.html
<div class="note-wrapper"> <div class="note-content"> <div class="notes-header"> <h1 title="Notes">Notes</h1> </div> <div class="note-wrapper"> <a class="card-notes" ng-repeat="note in notes" ng-href="#/notes/{{note.id}}"> <nw-card header="note.title" description="note.description"></nw-card> </a> </div> </div> </div>
users/index.html
<div class="users-wrapper"> <h1>Users</h1> <div class="users-wrapper"> <a class="card-users" ng-repeat="user in users"> <nw-card header ="user.name" description="user.bio"></nw-card> </a> </div> </div>
_________________________________________________________________________________
2.12 Style the Tweeted Note
js/directives/nw-card.js
angular.module('NoteWrangler') .directive('nwCard', function() { return { restrict: 'E', templateUrl: './templates/directives/nw-card.html', scope: { header: '=', description: '=', tweeted: '=' }, link: function(scope,element){ if(scope.tweeted) { element.addClass('tweeted'); } } }; });
SS
3.2 A Factory Recipe I 250 PTS
js/services/note.js
angular.module('NoteWrangler').factory("Note",[function NoteFactory() { }]) ;
3.3 A Factory Recipe II
js/services/note.js
angular.module('NoteWrangler') .factory('Note', ['$http',function NoteFactory($http) { return { all:function(){ return $http({method: 'GET', url: '/notes'}); } }; }]);
js/controllers/notes-index-controller.js
angular.module('NoteWrangler') .controller('NotesIndexController', ['$scope', 'Note', function($scope, Note) { Note.all().success(function(data){ $scope.notes = data; }); }]);
3.5 A Tweetable Service
3.8 Configurable Bits Need a Provider
js/services/tweetable.js
angular.module('NoteWrangler') .provider('Tweetable', function TweetableProvider() { var characterLength = 144; this.setLength = function(maxLength) { characterLength = maxLength; }; this.$get = function($http) { return function(potentialTweet) { return $http({ method: 'POST', url: 'http://gentle-spire-1153.herokuapp.com/tweet', data: { description: potentialTweet, maxLength: characterLength } }); }; }; });
3.9 Configuring the Tweet Length
js/app.js
angular.module('NoteWrangler', ['ngRoute']) .config(function(TweetableProvider){ TweetableProvider.setLength(40); }) ;
3.11 $resource Refactor
js/services/note.js
angular.module('NoteWrangler') .factory('Note', ['$resource', function NoteFactory($resource) { return $resource("/notes",{},{}); }]);
js/app.js
angular.module('NoteWrangler', ['ngRoute','ngResource']) .config(function(TweetableProvider) { TweetableProvider.setLength(144); });
index.html
<html lang="en" ng-app="NoteWrangler"> <head> <meta charset="utf-8"> <title>Note Wrangler</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="css/application.css" /> </head> <body> <div class="nav-wrapper has-dropdown"> <div class="nav-content"> <div class="wrapper"> <div class="nav-content-layout"> <div class="nav-list"> <a href="#/notes" class="list-item" ng-click="selected = 'notes'" ng-class="{'active': selected == 'notes'}"> Notes </a> <a href="#/users" class="list-item" ng-click="selected = 'users'" ng-class="{'active': selected == 'users'}"> Users </a> </div> </div> </div> </div> </div> <div class="hero-wrapper"> <div class="hero-content"> <div class="hero"></div> </div> </div> <div class="main-wrapper"> <div ng-view></div> </div> <script src="./js/vendor/jquery.js"></script> <script src="./js/vendor/angular.js"></script> <script src="./js/vendor/angular-route.js"></script> <script src="./js/vendor/angular-resource.js"></script> <script src="./js/app.js"></script> <script src="./js/routes.js"></script> <script src="./js/controllers/notes-create-controller.js"></script> <script src="./js/controllers/notes-edit-controller.js"></script> <script src="./js/controllers/notes-index-controller.js"></script> <script src="./js/controllers/notes-show-controller.js"></script> <script src="./js/controllers/users-index-controller.js"></script> <script src="./js/controllers/users-show-controller.js"></script> <script src="./js/directives/nw-card.js"></script> <script src="./js/services/note.js"></script> <script src="./js/services/tweetable.js"></script> </body> </html>
______________________________________________________
3.12 Using a $resource-ful Note Service
Instead of calling the
all()
method of Note
, we want to assign $scope.notes
the value returned from the query()
method of Note
.$scope.notes = Note.query();
js/controllers/notes-index-controller.js
angular.module('NoteWrangler') .controller('NotesIndexController', ['$scope', 'Note', 'Tweetable', function($scope, Note, Tweetable) { $scope.notes = Note.query(); $scope.tweetThatNote = function(noteToTweet) { Tweetable(noteToTweet).success(function(status) { console.log(status); }); }; }]);
______________________________________________________
No comments:
Post a Comment