docs(architecture/dart): proofread, updated Dart/TS app and jade
closes #1807 - e2e tests now also cover the tax calculator. - Dart app updated to match TS (it had no sales tax calculator). - TS sample source cleanup (e.g. removed many unnecessary `docregions`). - Prose updated to include @kwalrath's revisions from a while ago, Ward's comments, and some of my edits as well. Contributes to #1598 and #1508.
This commit is contained in:
parent
81b011c6ba
commit
761f857f13
@ -0,0 +1,15 @@
|
|||||||
|
// #docregion import
|
||||||
|
import 'package:angular2/core.dart';
|
||||||
|
// #enddocregion import
|
||||||
|
|
||||||
|
import 'hero_list_component.dart';
|
||||||
|
import 'sales_tax_component.dart';
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: 'my-app',
|
||||||
|
template: '''
|
||||||
|
<hero-list></hero-list>
|
||||||
|
<sales-tax></sales-tax>''',
|
||||||
|
directives: const [HeroListComponent, SalesTaxComponent])
|
||||||
|
// #docregion export
|
||||||
|
class AppComponent { }
|
@ -1,4 +1,5 @@
|
|||||||
// #docregion
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
@ -6,19 +7,22 @@ import 'logger_service.dart';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class BackendService {
|
class BackendService {
|
||||||
|
static final _mockHeroes = [
|
||||||
|
new Hero('Windstorm', 'Weather mastery'),
|
||||||
|
new Hero('Mr. Nice', 'Killing them with kindness'),
|
||||||
|
new Hero('Magneta', 'Manipulates metalic objects')
|
||||||
|
];
|
||||||
|
|
||||||
final Logger _logger;
|
final Logger _logger;
|
||||||
List getAll(type) {
|
|
||||||
// TODO get from the database and return as a promise
|
|
||||||
if (type == Hero) {
|
|
||||||
return [
|
|
||||||
new Hero('Windstorm', power: 'Weather mastery'),
|
|
||||||
new Hero('Mr. Nice', power: 'Killing them with kindness'),
|
|
||||||
new Hero('Magneta', power: 'Manipulates metalic objects')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
_logger.error('Cannot get object of this type');
|
|
||||||
throw new ArgumentError("TODO: put log content here");
|
|
||||||
}
|
|
||||||
|
|
||||||
BackendService(Logger this._logger);
|
BackendService(Logger this._logger);
|
||||||
|
|
||||||
|
Future<List> getAll(type) {
|
||||||
|
// TODO get from the database
|
||||||
|
if (type == Hero) return new Future.value(_mockHeroes);
|
||||||
|
|
||||||
|
var msg = 'Cannot get object of this type';
|
||||||
|
_logger.error(msg);
|
||||||
|
throw new Exception(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
// #docregion
|
|
||||||
class Hero {
|
class Hero {
|
||||||
static int _nextId = 1;
|
static int _nextId = 1;
|
||||||
int id;
|
final int id;
|
||||||
String name;
|
String name, power;
|
||||||
String power;
|
|
||||||
|
|
||||||
Hero(this.name, {this.power}) {
|
Hero(this.name, [this.power = '']) : id = _nextId++;
|
||||||
id = _nextId++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
// #docregion
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
|
|
||||||
@Component(selector: 'hero-detail', templateUrl: 'hero_detail_component.html')
|
@Component(
|
||||||
|
selector: 'hero-detail',
|
||||||
|
templateUrl: 'hero_detail_component.html')
|
||||||
class HeroDetailComponent {
|
class HeroDetailComponent {
|
||||||
@Input() Hero hero;
|
@Input()
|
||||||
|
Hero hero;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<h4>{{hero.name}} Detail</h4>
|
<h4>{{hero.name}} Detail</h4>
|
||||||
<div>Id: {{hero.id}}</div>
|
<div>Id: {{hero.id}}</div>
|
||||||
<div>Name:
|
<div>Name:
|
||||||
<!-- #docregion ng-model -->
|
<!-- #docregion ngModel -->
|
||||||
<input [(ngModel)]="hero.name"></div>
|
<input [(ngModel)]="hero.name">
|
||||||
<!-- #enddocregion ng-model -->
|
<!-- #enddocregion ngModel -->
|
||||||
<div>Power:<input [(ngModel)]="hero.power">
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>Power:<input [(ngModel)]="hero.power"></div>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// #docplaster
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
@ -6,32 +5,31 @@ import 'hero_detail_component.dart';
|
|||||||
import 'hero_service.dart';
|
import 'hero_service.dart';
|
||||||
|
|
||||||
// #docregion metadata
|
// #docregion metadata
|
||||||
// #docregion providers
|
|
||||||
@Component(
|
@Component(
|
||||||
// #enddocregion providers
|
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
templateUrl: 'hero_list_component.html',
|
templateUrl: 'hero_list_component.html',
|
||||||
directives: const [HeroDetailComponent],
|
directives: const [HeroDetailComponent],
|
||||||
// #docregion providers
|
// #docregion providers
|
||||||
providers: const [HeroService])
|
providers: const [HeroService]
|
||||||
// #enddocregion providers
|
// #enddocregion providers
|
||||||
// #enddocregion metadata
|
)
|
||||||
/*
|
|
||||||
// #docregion metadata, providers
|
|
||||||
class HeroListComponent { ... }
|
|
||||||
// #enddocregion metadata, providers
|
|
||||||
*/
|
|
||||||
// #docregion class
|
// #docregion class
|
||||||
class HeroListComponent {
|
class HeroListComponent implements OnInit {
|
||||||
|
// #enddocregion metadata
|
||||||
List<Hero> heroes;
|
List<Hero> heroes;
|
||||||
Hero selectedHero;
|
Hero selectedHero;
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
HeroListComponent(HeroService heroService) {
|
final HeroService _heroService;
|
||||||
heroes = heroService.getHeroes();
|
|
||||||
|
HeroListComponent(this._heroService);
|
||||||
|
// #enddocregion ctor
|
||||||
|
|
||||||
|
void ngOnInit() {
|
||||||
|
heroes = _heroService.getHeroes();
|
||||||
}
|
}
|
||||||
// #enddocregion ctor
|
|
||||||
selectHero(Hero hero) {
|
void selectHero(Hero hero) {
|
||||||
selectedHero = hero;
|
selectedHero = hero;
|
||||||
}
|
}
|
||||||
|
// #docregion metadata
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
<!-- #docregion -->
|
<!-- #docregion -->
|
||||||
<h2>Hero List</h2>
|
<h2>Hero List</h2>
|
||||||
|
|
||||||
<div *ngFor="let hero of heroes" (click)="selectHero(hero)">
|
<p><i>Pick a hero from the list</i></p>
|
||||||
{{hero.name}}
|
<ul>
|
||||||
</div>
|
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
|
||||||
|
{{hero.name}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<hero-detail *ngIf="selectedHero != null" [hero]="selectedHero"></hero-detail>
|
<hero-detail *ngIf="selectedHero != null" [hero]="selectedHero"></hero-detail>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
<!--#docregion binding -->
|
<!--#docregion binding -->
|
||||||
<div ... >{{hero.name}}</div>
|
<li>{{hero.name}}</li>
|
||||||
<hero-detail ... [hero]="selectedHero"></hero-detail>
|
<hero-detail [hero]="selectedHero"></hero-detail>
|
||||||
<div ... (click)="selectHero(hero)">...</div>
|
<li (click)="selectHero(hero)"></li>
|
||||||
<!--#enddocregion binding -->
|
<!--#enddocregion binding -->
|
||||||
|
|
||||||
<!--#docregion structural -->
|
<!--#docregion structural -->
|
||||||
<div *ngFor="let hero of heroes" ...>...</div>
|
<li *ngFor="let hero of heroes"></li>
|
||||||
<hero-detail *ngIf="selectedHero != null" ...></hero-detail>
|
<hero-detail *ngIf="selectedHero != null"></hero-detail>
|
||||||
<!--#enddocregion structural -->
|
|
||||||
|
@ -4,16 +4,20 @@ import 'backend_service.dart';
|
|||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'logger_service.dart';
|
import 'logger_service.dart';
|
||||||
|
|
||||||
// #docregion class
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
// #docregion class
|
||||||
class HeroService {
|
class HeroService {
|
||||||
final BackendService _backendService;
|
final BackendService _backendService;
|
||||||
final Logger _logger;
|
final Logger _logger;
|
||||||
HeroService(Logger this._logger, BackendService this._backendService);
|
final List<Hero> heroes = [];
|
||||||
|
|
||||||
|
HeroService(this._logger, this._backendService);
|
||||||
|
|
||||||
List<Hero> getHeroes() {
|
List<Hero> getHeroes() {
|
||||||
List<Hero> heroes = _backendService.getAll(Hero);
|
_backendService.getAll(Hero).then((heroes) {
|
||||||
_logger.log('Got ${heroes.length} heroes from the server.');
|
_logger.log('Fetched ${heroes.length} heroes.');
|
||||||
|
this.heroes.addAll(heroes); // fill cache
|
||||||
|
});
|
||||||
return heroes;
|
return heroes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
// #docregion
|
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
/// A service for logging messages of various types.
|
|
||||||
///
|
|
||||||
/// We could switch this implementation to use package:logging.
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
// #docregion class
|
||||||
class Logger {
|
class Logger {
|
||||||
void log(Object msg) => window.console.log(msg);
|
void log(Object msg) => window.console.log(msg);
|
||||||
|
|
||||||
void error(Object msg) => window.console.error(msg);
|
void error(Object msg) => window.console.error(msg);
|
||||||
|
|
||||||
void warn(Object msg) => window.console.warn(msg);
|
void warn(Object msg) => window.console.warn(msg);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
import 'sales_tax_service.dart';
|
||||||
|
import 'tax_rate_service.dart';
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: 'sales-tax',
|
||||||
|
template: '''
|
||||||
|
<h2>Sales Tax Calculator</h2>
|
||||||
|
Amount: <input #amountBox (change)="0">
|
||||||
|
|
||||||
|
<div *ngIf="amountBox.value != ''">
|
||||||
|
The sales tax is
|
||||||
|
{{ getTax(amountBox.value) | currency:'USD':false:'1.2-2' }}
|
||||||
|
<!-- would like to write currency:'USD':true:'1.2-2' but
|
||||||
|
currency as symbol is not currently supported; see
|
||||||
|
https://github.com/dart-lang/intl/issues/59 -->
|
||||||
|
</div>
|
||||||
|
''',
|
||||||
|
providers: const [SalesTaxService, TaxRateService])
|
||||||
|
class SalesTaxComponent {
|
||||||
|
SalesTaxService _salesTaxService;
|
||||||
|
|
||||||
|
SalesTaxComponent(this._salesTaxService) {}
|
||||||
|
|
||||||
|
num getTax(dynamic /* String | num */ value) =>
|
||||||
|
this._salesTaxService.getVAT(value);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
import 'tax_rate_service.dart';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
class SalesTaxService {
|
||||||
|
TaxRateService rateService;
|
||||||
|
|
||||||
|
SalesTaxService(this.rateService);
|
||||||
|
|
||||||
|
num getVAT(dynamic /* String | num */ value) =>
|
||||||
|
rateService.getRate('VAT') *
|
||||||
|
(value is num ? value : num.parse(value, (_) => 0));
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
class TaxRateService {
|
||||||
|
getRate(String rateName) => 0.10;
|
||||||
|
}
|
@ -1,12 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Intro to Angular 2</title>
|
<title>Architecture of Angular 2</title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<meta charset="UTF-8">
|
||||||
<script defer src="main.dart" type="application/dart"></script>
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<script defer src="packages/browser/dart.js"></script>
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
|
||||||
|
<script defer src="main.dart" type="application/dart"></script>
|
||||||
|
<script defer src="packages/browser/dart.js"></script>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<hero-list>Loading...</hero-list>
|
<my-app>Loading...</my-app>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import 'package:angular2/platform/browser.dart';
|
import 'package:angular2/platform/browser.dart';
|
||||||
|
// #docregion import
|
||||||
|
import 'package:developer_guide_intro/app_component.dart';
|
||||||
|
// #enddocregion import
|
||||||
import 'package:developer_guide_intro/backend_service.dart';
|
import 'package:developer_guide_intro/backend_service.dart';
|
||||||
import 'package:developer_guide_intro/hero_list_component.dart';
|
|
||||||
import 'package:developer_guide_intro/hero_service.dart';
|
import 'package:developer_guide_intro/hero_service.dart';
|
||||||
import 'package:developer_guide_intro/logger_service.dart';
|
import 'package:developer_guide_intro/logger_service.dart';
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
// #docregion bootstrap
|
// #docregion bootstrap
|
||||||
bootstrap(HeroListComponent, [BackendService, HeroService, Logger]);
|
bootstrap(AppComponent, [BackendService, HeroService, Logger]);
|
||||||
// #enddocregion bootstrap
|
// #enddocregion bootstrap
|
||||||
}
|
}
|
||||||
|
@ -1,63 +1,99 @@
|
|||||||
/// <reference path='../_protractor/e2e.d.ts' />
|
/// <reference path='../_protractor/e2e.d.ts' />
|
||||||
'use strict';
|
'use strict';
|
||||||
describe('Architecture', function () {
|
|
||||||
|
|
||||||
let title = 'Hero List';
|
const nameSuffix = 'X';
|
||||||
|
|
||||||
beforeAll(function () {
|
class Hero {
|
||||||
browser.get('');
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Architecture', () => {
|
||||||
|
|
||||||
|
const expectedTitle = 'Architecture of Angular 2';
|
||||||
|
const expectedH2 = ['Hero List', 'Sales Tax Calculator'];
|
||||||
|
|
||||||
|
beforeAll(() => browser.get(''));
|
||||||
|
|
||||||
|
it(`has title '${expectedTitle}'`, () => {
|
||||||
|
expect(browser.getTitle()).toEqual(expectedTitle);
|
||||||
});
|
});
|
||||||
|
|
||||||
function itReset(name: string, func: () => any) {
|
it(`has h2 '${expectedH2}'`, () => {
|
||||||
it(name, function() {
|
let h2 = element.all(by.css('h2')).map((elt) => elt.getText());
|
||||||
browser.get('').then(func);
|
expect(h2).toEqual(expectedH2);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it(`should display correct title: ${title}`, function () {
|
|
||||||
expect(element(by.css('h2')).getText()).toEqual(title);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display correct detail after selection', function() {
|
|
||||||
let detailView = element(by.css('hero-detail'));
|
|
||||||
expect(detailView.isPresent()).toBe(false);
|
|
||||||
// select the 2nd element
|
|
||||||
let selectEle = element.all(by.css('hero-list > div')).get(1);
|
|
||||||
selectEle.click().then(function() {
|
|
||||||
return selectEle.getText();
|
|
||||||
}).then(function(selectedHeroName) {
|
|
||||||
// works but too specific if we change the app
|
|
||||||
// expect(selectedHeroName).toEqual('Mr. Nice');
|
|
||||||
expect(detailView.isDisplayed()).toBe(true);
|
|
||||||
let detailTitleEle = element(by.css('hero-detail > h4'));
|
|
||||||
expect(detailTitleEle.getText()).toContain(selectedHeroName);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
itReset('should display correct detail after modification', function() {
|
|
||||||
let detailView = element(by.css('hero-detail'));
|
|
||||||
expect(detailView.isPresent()).toBe(false);
|
|
||||||
// select the 2nd element
|
|
||||||
let selectEle = element.all(by.css('hero-list > div')).get(1);
|
|
||||||
selectEle.click().then(function () {
|
|
||||||
return selectEle.getText();
|
|
||||||
}).then(function (selectedHeroName) {
|
|
||||||
let detailTitleEle = element(by.css('hero-detail > h4'));
|
|
||||||
expect(detailTitleEle.getText()).toContain(selectedHeroName);
|
|
||||||
let heroNameEle = element.all(by.css('hero-detail input')).get(0);
|
|
||||||
|
|
||||||
// check that both the initial selected item and the detail title reflect changes
|
|
||||||
// made to the input box.
|
|
||||||
// heroNameEle.sendKeys('foo');
|
|
||||||
sendKeys(heroNameEle, 'foo');
|
|
||||||
expect(detailTitleEle.getText()).toContain('foo');
|
|
||||||
expect(selectEle.getText()).toContain('foo');
|
|
||||||
|
|
||||||
// getText on an input element always returns null
|
|
||||||
// http://stackoverflow.com/questions/20310442/how-to-gettext-on-an-input-in-protractor
|
|
||||||
// expect(heroNameEle.getText()).toEqual(selectedHeroName);
|
|
||||||
expect(heroNameEle.getAttribute('value')).toEqual(selectedHeroName + 'foo');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Hero', heroTests);
|
||||||
|
describe('Salex tax', salesTaxTests);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function heroTests() {
|
||||||
|
|
||||||
|
const targetHero: Hero = { id: 2, name: 'Mr. Nice' };
|
||||||
|
|
||||||
|
it('has the right number of heroes', () => {
|
||||||
|
let page = getPageElts();
|
||||||
|
expect(page.heroes.count()).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has no hero details initially', function () {
|
||||||
|
let page = getPageElts();
|
||||||
|
expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows selected hero details', async () => {
|
||||||
|
await element(by.cssContainingText('li', targetHero.name)).click();
|
||||||
|
let page = getPageElts();
|
||||||
|
let hero = await heroFromDetail(page.heroDetail);
|
||||||
|
expect(hero.id).toEqual(targetHero.id);
|
||||||
|
expect(hero.name).toEqual(targetHero.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`shows updated hero name in details`, async () => {
|
||||||
|
let input = element.all(by.css('input')).first();
|
||||||
|
await sendKeys(input, nameSuffix);
|
||||||
|
let page = getPageElts();
|
||||||
|
let hero = await heroFromDetail(page.heroDetail);
|
||||||
|
let newName = targetHero.name + nameSuffix;
|
||||||
|
expect(hero.id).toEqual(targetHero.id);
|
||||||
|
expect(hero.name).toEqual(newName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function salesTaxTests() {
|
||||||
|
it('has no sales tax initially', function () {
|
||||||
|
let page = getPageElts();
|
||||||
|
expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows sales tax', async function () {
|
||||||
|
let page = getPageElts();
|
||||||
|
await sendKeys(page.salesTaxAmountInput, '10');
|
||||||
|
// Note: due to Dart bug USD is shown instead of $
|
||||||
|
let re = /The sales tax is (\$|USD)1.00/;
|
||||||
|
expect(page.salesTaxDetail.getText()).toMatch(re);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
function getPageElts() {
|
||||||
|
return {
|
||||||
|
heroes: element.all(by.css('my-app li')),
|
||||||
|
heroDetail: element(by.css('my-app hero-detail')),
|
||||||
|
salesTaxAmountInput: element(by.css('my-app sales-tax input')),
|
||||||
|
salesTaxDetail: element(by.css('my-app sales-tax div'))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function heroFromDetail(detail: protractor.ElementFinder): Promise<Hero> {
|
||||||
|
// Get hero id from the first <div>
|
||||||
|
let _id = await detail.all(by.css('div')).first().getText();
|
||||||
|
// Get name from the h2
|
||||||
|
let _name = await detail.element(by.css('h4')).getText();
|
||||||
|
return {
|
||||||
|
id: +_id.substr(_id.indexOf(' ') + 1),
|
||||||
|
name: _name.substr(0, _name.lastIndexOf(' '))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<h4>{{hero.name}} Detail</h4>
|
<h4>{{hero.name}} Detail</h4>
|
||||||
<div>Id: {{hero.id}}</div>
|
<div>Id: {{hero.id}}</div>
|
||||||
<div>Name:
|
<div>Name:
|
||||||
<!-- #docregion ngModel -->
|
<!-- #docregion ngModel -->
|
||||||
<input [(ngModel)]="hero.name">
|
<input [(ngModel)]="hero.name">
|
||||||
<!-- #enddocregion ngModel -->
|
<!-- #enddocregion ngModel -->
|
||||||
</div>
|
</div>
|
||||||
<div>Power:<input [(ngModel)]="hero.power"></div>
|
<div>Power:<input [(ngModel)]="hero.power"></div>
|
@ -1,12 +1,9 @@
|
|||||||
<!--#docregion binding -->
|
<!--#docregion binding -->
|
||||||
<div>{{hero.name}}</div>
|
<li>{{hero.name}}</li>
|
||||||
<hero-detail [hero]="selectedHero"></hero-detail>
|
<hero-detail [hero]="selectedHero"></hero-detail>
|
||||||
<div (click)="selectHero(hero)"></div>
|
<li (click)="selectHero(hero)"></li>
|
||||||
|
|
||||||
<!--#enddocregion binding -->
|
<!--#enddocregion binding -->
|
||||||
|
|
||||||
<!--#docregion structural -->
|
<!--#docregion structural -->
|
||||||
<div *ngFor="let hero of heroes"></div>
|
<li *ngFor="let hero of heroes"></li>
|
||||||
<hero-detail *ngIf="selectedHero"></hero-detail>
|
<hero-detail *ngIf="selectedHero"></hero-detail>
|
||||||
|
|
||||||
<!--#enddocregion structural -->
|
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
<h2>Hero List</h2>
|
<h2>Hero List</h2>
|
||||||
|
|
||||||
<p><i>Pick a hero from the list</i></p>
|
<p><i>Pick a hero from the list</i></p>
|
||||||
<div *ngFor="let hero of heroes" (click)="selectHero(hero)">
|
<ul>
|
||||||
{{hero.name}}
|
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
|
||||||
</div>
|
{{hero.name}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
|
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// #docplaster
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
@ -6,35 +5,28 @@ import { HeroDetailComponent } from './hero-detail.component';
|
|||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
// #docregion metadata
|
// #docregion metadata
|
||||||
// #docregion providers
|
|
||||||
@Component({
|
@Component({
|
||||||
// #enddocregion providers
|
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
templateUrl: 'app/hero-list.component.html',
|
templateUrl: 'app/hero-list.component.html',
|
||||||
directives: [HeroDetailComponent],
|
directives: [HeroDetailComponent],
|
||||||
// #docregion providers
|
// #docregion providers
|
||||||
providers: [HeroService]
|
providers: [HeroService]
|
||||||
|
// #enddocregion providers
|
||||||
})
|
})
|
||||||
// #enddocregion providers
|
|
||||||
// #enddocregion metadata
|
|
||||||
/*
|
|
||||||
// #docregion metadata, providers
|
|
||||||
export class HeroesComponent { ... }
|
|
||||||
// #enddocregion metadata, providers
|
|
||||||
*/
|
|
||||||
// #docregion class
|
// #docregion class
|
||||||
export class HeroListComponent implements OnInit {
|
export class HeroListComponent implements OnInit {
|
||||||
|
// #enddocregion metadata
|
||||||
heroes: Hero[];
|
heroes: Hero[];
|
||||||
selectedHero: Hero;
|
selectedHero: Hero;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(private service: HeroService) { }
|
constructor(private service: HeroService) { }
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.heroes = this.service.getHeroes();
|
this.heroes = this.service.getHeroes();
|
||||||
}
|
}
|
||||||
|
|
||||||
selectHero(hero: Hero) { this.selectedHero = hero; }
|
selectHero(hero: Hero) { this.selectedHero = hero; }
|
||||||
|
// #docregion metadata
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
@ -9,11 +9,9 @@ import { Logger } from './logger.service';
|
|||||||
export class HeroService {
|
export class HeroService {
|
||||||
private heroes: Hero[] = [];
|
private heroes: Hero[] = [];
|
||||||
|
|
||||||
// #docregion ctor
|
|
||||||
constructor(
|
constructor(
|
||||||
private backend: BackendService,
|
private backend: BackendService,
|
||||||
private logger: Logger) { }
|
private logger: Logger) { }
|
||||||
// #enddocregion ctor
|
|
||||||
|
|
||||||
getHeroes() {
|
getHeroes() {
|
||||||
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
|
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
|
||||||
@ -23,4 +21,3 @@ export class HeroService {
|
|||||||
return this.heroes;
|
return this.heroes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// #docregion
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -8,4 +7,3 @@ export class Logger {
|
|||||||
error(msg: any) { console.error(msg); }
|
error(msg: any) { console.error(msg); }
|
||||||
warn(msg: any) { console.warn(msg); }
|
warn(msg: any) { console.warn(msg); }
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
// #docplaster
|
|
||||||
// #docregion
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { SalesTaxService } from './sales-tax.service';
|
import { SalesTaxService } from './sales-tax.service';
|
||||||
import { TaxRateService } from './tax-rate.service';
|
import { TaxRateService } from './tax-rate.service';
|
||||||
|
|
||||||
// #docregion metadata
|
|
||||||
// #docregion providers
|
|
||||||
@Component({
|
@Component({
|
||||||
// #enddocregion providers
|
|
||||||
selector: 'sales-tax',
|
selector: 'sales-tax',
|
||||||
template: `
|
template: `
|
||||||
<h2>Sales Tax Calculator</h2>
|
<h2>Sales Tax Calculator</h2>
|
||||||
@ -19,24 +14,12 @@ import { TaxRateService } from './tax-rate.service';
|
|||||||
{{ getTax(amountBox.value) | currency:'USD':true:'1.2-2' }}
|
{{ getTax(amountBox.value) | currency:'USD':true:'1.2-2' }}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
// #docregion providers
|
|
||||||
providers: [SalesTaxService, TaxRateService]
|
providers: [SalesTaxService, TaxRateService]
|
||||||
})
|
})
|
||||||
// #enddocregion providers
|
|
||||||
// #enddocregion metadata
|
|
||||||
/*
|
|
||||||
// #docregion metadata, providers
|
|
||||||
export class SalesTaxComponent { ... }
|
|
||||||
// #enddocregion metadata, providers
|
|
||||||
*/
|
|
||||||
// #docregion class
|
|
||||||
export class SalesTaxComponent {
|
export class SalesTaxComponent {
|
||||||
// #docregion ctor
|
|
||||||
constructor(private salesTaxService: SalesTaxService) { }
|
constructor(private salesTaxService: SalesTaxService) { }
|
||||||
// #enddocregion ctor
|
|
||||||
|
|
||||||
getTax(value: string | number) {
|
getTax(value: string | number) {
|
||||||
return this.salesTaxService.getVAT(value);
|
return this.salesTaxService.getVAT(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
// #docregion
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { TaxRateService } from './tax-rate.service';
|
import { TaxRateService } from './tax-rate.service';
|
||||||
|
|
||||||
// #docregion class
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SalesTaxService {
|
export class SalesTaxService {
|
||||||
constructor(private rateService: TaxRateService) { }
|
constructor(private rateService: TaxRateService) { }
|
||||||
|
|
||||||
getVAT(value: string | number) {
|
getVAT(value: string | number) {
|
||||||
let amount: number;
|
let amount = (typeof value === 'string') ?
|
||||||
if (typeof value === 'string') {
|
parseFloat(value) : value;
|
||||||
amount = parseFloat(value);
|
|
||||||
} else {
|
|
||||||
amount = value;
|
|
||||||
}
|
|
||||||
return (amount || 0) * this.rateService.getRate('VAT');
|
return (amount || 0) * this.rateService.getRate('VAT');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
// #docregion
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
// #docregion class
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TaxRateService {
|
export class TaxRateService {
|
||||||
getRate(rateName: string) {return 0.10; } // always 10% everywhere
|
getRate(rateName: string) { return 0.10; } // 10% everywhere
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
|
||||||
|
1
public/docs/dart/latest/guide/animations.jade
Normal file
1
public/docs/dart/latest/guide/animations.jade
Normal file
@ -0,0 +1 @@
|
|||||||
|
!= partial("../../../_includes/_ts-temp")
|
@ -1,451 +1,72 @@
|
|||||||
include ../_util-fns
|
extends ../../../ts/latest/guide/architecture.jade
|
||||||
|
|
||||||
|
block includes
|
||||||
|
include ../_util-fns
|
||||||
|
- var _library_module = 'library'
|
||||||
|
- var _at_angular = 'angular2'
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Angular 2 is a framework to help us build client applications in HTML and
|
Angular 2 is a framework to help us build client applications in HTML and
|
||||||
either JavaScript or a language (like Dart or TypeScript) that compiles to JavaScript.
|
either JavaScript or a language (like Dart or TypeScript) that compiles to JavaScript.
|
||||||
Angular 2 for Dart is published as the `angular2` package, which
|
|
||||||
(like many other Dart packages) is available via the Pub tool.
|
|
||||||
|
|
||||||
With Angular, we write applications by composing HTML *templates* with Angularized markup,
|
block angular-parts
|
||||||
writing *component* classes to manage those templates, adding application logic in *services*,
|
|
||||||
and handing the top root component to Angular's *bootstrapper*.
|
|
||||||
|
|
||||||
Angular takes over, presenting our application content in a browser and responding to user interactions
|
|
||||||
according to the instructions we provided.
|
|
||||||
|
|
||||||
<!-- figure img(src="/resources/images/devguide/architecture/airplane.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) -->
|
|
||||||
:marked
|
|
||||||
Of course there is more to it than this.
|
|
||||||
We're cruising at high altitude in this overview.
|
|
||||||
We're looking for landmarks. We should expect the object below to be fuzzy and obscured by occasional clouds.
|
|
||||||
Details become more clear and precise when we land in the chapters themselves.
|
|
||||||
<br clear="all">
|
|
||||||
|
|
||||||
:marked
|
|
||||||
An Angular 2 for Dart application rests on seven main building blocks:
|
|
||||||
1. [Components](#component)
|
|
||||||
1. [Templates](#template)
|
|
||||||
1. [Metadata](#metadata)
|
|
||||||
1. [Data binding](#data-binding)
|
|
||||||
1. [Directives](#directive)
|
|
||||||
1. [Services](#service)
|
|
||||||
1. [Dependency injection](#dependency-injection)
|
|
||||||
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/overview.png" alt="overview" style="margin-left:-40px;" width="700")
|
|
||||||
:marked
|
|
||||||
Learn these seven and we're on our way.
|
|
||||||
|
|
||||||
.l-main-section
|
|
||||||
<a id="component"></a>
|
|
||||||
:marked
|
|
||||||
## Components
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
|
|
||||||
:marked
|
|
||||||
A **component** controls a patch of screen real estate that we could call a *view*.
|
|
||||||
A set of navigation links, a list of heroes, a hero editor ...
|
|
||||||
they're all views controlled by components.
|
|
||||||
|
|
||||||
We define a component's application logic — what it does to support the view — inside a class.
|
|
||||||
The class interacts with the view through an API of properties and methods.
|
|
||||||
|
|
||||||
<a id="component-code"></a>
|
|
||||||
A `HeroListComponent`, for example, might have a `heroes` property that returns an array of heroes
|
|
||||||
that it acquired from a service.
|
|
||||||
It might have a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
|
||||||
The component might be a class like this:
|
|
||||||
|
|
||||||
+makeExample('architecture/dart/lib/hero_list_component.dart', 'class', 'lib/hero_list_component.dart')
|
|
||||||
:marked
|
|
||||||
Angular creates, updates, and destroys components as the user moves through the application.
|
|
||||||
The developer can take action at each moment in this lifecycle through optional lifecycle hooks.
|
|
||||||
<!-- PENDING: What was that supposed to link to? -->
|
|
||||||
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
:marked
|
||||||
We may wonder who is calling the component's constructor? Who provides the service parameter?
|
Angular 2 for Dart is published as the `angular2` package, which
|
||||||
For the moment, have faith that Angular will call the constructor and deliver an
|
(like many other Dart packages) is available via the Pub tool.
|
||||||
appropriate `HeroService` when we need it.
|
|
||||||
|
|
||||||
.l-main-section
|
block modules-in-dart
|
||||||
<a id="template"></a>
|
.callout.is-helpful
|
||||||
:marked
|
header Dart difference: Modules are compilation units or packages
|
||||||
## Templates
|
:marked
|
||||||
figure
|
In this guide, the term _module_ refers to a Dart compilation unit, such
|
||||||
img(src="/resources/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
|
as a library, or a package. (If a Dart file has no `library` or `part`
|
||||||
:marked
|
directive, then that file itself is a library and thus a compilation
|
||||||
We define a component's view with its companion **template**. A template is a form of HTML
|
unit.) For more information about compilation units, see
|
||||||
that tells Angular how to render the component.
|
the chapter on "Libraries and Scripts" in the
|
||||||
|
[Dart Language Specification](https://www.dartlang.org/docs/spec/).
|
||||||
|
|
||||||
A template looks like regular HTML much of the time ... and then it gets a bit strange. Here is a
|
block modules-are-optional
|
||||||
template for our `HeroListComponent`:
|
//- N/A
|
||||||
+makeExample('architecture/dart/lib/hero_list_component.html', null, 'lib/hero_list_component.html')
|
|
||||||
:marked
|
|
||||||
This template features typical HTML elements like `<h2>` and `<div>`.
|
|
||||||
But what are `*ngFor`, <code>{‌{hero.name}}</code>, `(click)`, `[hero]`, and `<hero-detail>`?
|
|
||||||
They're examples of Angular's template syntax. <!-- TODO: link to template-syntax.html -->
|
|
||||||
We'll grow accustomed to that syntax and may even learn to love it.
|
|
||||||
|
|
||||||
Take a look at the last line,
|
block export-qualifier
|
||||||
which has the `<hero-detail>` tag.
|
.callout.is-helpful
|
||||||
That tag adds a custom element representing a component we haven't seen yet,
|
header Dart difference: Public names are exported by default
|
||||||
a `HeroDetailComponent`.
|
:marked
|
||||||
|
Contrary to TypeScript, a Dart library always exports all names and
|
||||||
|
declarations in its **public** namespace, making explicit `export`
|
||||||
|
qualifiers unnecessary.
|
||||||
|
|
||||||
The `HeroDetailComponent` is a *different* component than the `HeroListComponent` we've seen.
|
When we say that a module _exports_ a declaration, we mean that the
|
||||||
The `HeroDetailComponent` (code not shown) presents facts about a particular hero, the
|
declaration is _public_. For more details about name spaces and export
|
||||||
hero that the user selects from the list presented by the `HeroListComponent`.
|
statements, see the section on "Exports" in the
|
||||||
The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
|
[Dart Language Specification](https://www.dartlang.org/docs/spec/).
|
||||||
|
|
||||||
figure
|
block ts-import
|
||||||
img(src="/resources/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px" )
|
//- N/A
|
||||||
:marked
|
|
||||||
Notice how `<hero-detail>` rests comfortably among native HTML elements.
|
|
||||||
We can and _will_ mix our custom components with native HTML in the same layouts.
|
|
||||||
|
|
||||||
In this manner we'll compose complex component trees to build out our richly featured application.
|
block angular-library-modules
|
||||||
<br clear="all">
|
|
||||||
|
|
||||||
.l-main-section
|
|
||||||
<a id="metadata"></a>
|
|
||||||
:marked
|
|
||||||
## Metadata
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px" )
|
|
||||||
:marked
|
|
||||||
<p style="padding-top:10px">Metadata tells Angular how to process a class.</p>
|
|
||||||
<br clear="all">
|
|
||||||
:marked
|
|
||||||
[Looking back at the code](#component-code) for `HeroListComponent`, we see that it's just a class.
|
|
||||||
There is no evidence of a framework, no "Angular" in it at all.
|
|
||||||
|
|
||||||
In fact, it really is *just a class*. It's not a component until we *tell Angular about it*.
|
|
||||||
|
|
||||||
We tell Angular that `HeroListComponent` is a component by attaching **metadata** to the class.
|
|
||||||
|
|
||||||
In Dart, we attach metadata by using an **annotation**.
|
|
||||||
Here's some metadata for `HeroListComponent`:
|
|
||||||
|
|
||||||
+makeExample('architecture/dart/lib/hero_list_component.dart', 'metadata', 'lib/hero_list_component.dart')
|
|
||||||
:marked
|
|
||||||
Here we see the `@Component` annotation, which (no surprise) identifies the class
|
|
||||||
immediately below it as a component class.
|
|
||||||
|
|
||||||
Annotations often have configuration parameters.
|
|
||||||
The `@Component` annotation takes parameters to provide the
|
|
||||||
information Angular needs to create and present the component and its view.
|
|
||||||
|
|
||||||
Here we see a few of the possible `@Component` parameters:
|
|
||||||
|
|
||||||
* `selector`: A CSS selector that tells Angular to create and insert an instance of this component
|
|
||||||
where it finds a `<hero-list>` tag in *parent* HTML.
|
|
||||||
For example, if an app's HTML contains `<hero-list></hero-list>`, then
|
|
||||||
Angular inserts an instance of the `HeroListComponent` view between those tags.
|
|
||||||
|
|
||||||
* `templateUrl`: The address of this component's template, which we showed [above](#the-template).
|
|
||||||
|
|
||||||
* `directives`: An array of the components or directives that *this* template requires.
|
|
||||||
We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent`
|
|
||||||
in the space indicated by `<hero-detail>` tags.
|
|
||||||
Angular will do so only if we mention the `HeroDetailComponent` in this `directives` array.
|
|
||||||
|
|
||||||
* `providers`: An array of **dependency injection providers** for services that the component requires.
|
|
||||||
This is one way to tell Angular that our component's constructor requires a `HeroService`
|
|
||||||
so it can get the list of heroes to display. We'll get to dependency injection later.
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px" )
|
|
||||||
|
|
||||||
:marked
|
|
||||||
At runtime, Angular discovers the metadata specified by the `@Component`
|
|
||||||
annotation. That's how Angular learns how to do "the right thing".
|
|
||||||
|
|
||||||
The template, metadata, and component together describe the view.
|
|
||||||
|
|
||||||
We apply other metadata annotations in a similar fashion to guide Angular behavior.
|
|
||||||
`@Injectable`, `@Input`, `@Output`, and `@RouterConfig` are a few of the more popular annotations
|
|
||||||
we'll master as our Angular knowledge grows.
|
|
||||||
<br clear="all">
|
|
||||||
:marked
|
|
||||||
The architectural takeaway is that we must add metadata to our code
|
|
||||||
so that Angular knows what to do.
|
|
||||||
|
|
||||||
.l-main-section
|
|
||||||
<a id="data-binding"></a>
|
|
||||||
:marked
|
|
||||||
## Data binding
|
|
||||||
Without a framework, we would be responsible for pushing data values into the HTML controls and turning user responses
|
|
||||||
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to
|
|
||||||
read as any experienced jQuery programmer can attest.
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px" )
|
|
||||||
:marked
|
|
||||||
Angular supports **data binding**,
|
|
||||||
a mechanism for coordinating parts of a template with parts of a component.
|
|
||||||
We add binding markup to the template HTML to tell Angular how to connect both sides.
|
|
||||||
|
|
||||||
There are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions —
|
|
||||||
as indicated by the arrows in the diagram.
|
|
||||||
<br clear="all">
|
|
||||||
:marked
|
|
||||||
We saw three forms of data binding in our [example](#template) template:
|
|
||||||
+makeExample('architecture/dart/lib/hero_list_component_1.html', 'binding')(format=".")
|
|
||||||
:marked
|
|
||||||
* The <code>{‌{hero.name}}</code> [interpolation](displaying-data.html#interpolation)
|
|
||||||
displays the component's `hero.name` property value within the `<div>` tags.
|
|
||||||
|
|
||||||
* The `[hero]` property binding <!-- TODO: link to template-syntax.html#property-binding-->
|
|
||||||
passes the value of `selectedHero` from
|
|
||||||
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
|
|
||||||
|
|
||||||
* The `(click)` [event binding](user-input.html#click) calls the component's `selectHero` method when the user clicks a hero's name.
|
|
||||||
|
|
||||||
**Two-way data binding** is an important fourth form
|
|
||||||
that combines property and event binding in a single notation, using the `ngModel` directive.
|
|
||||||
We didn't have a two-way binding in the `HeroListComponent` template;
|
|
||||||
here's an example from the `HeroDetailComponent` template:
|
|
||||||
|
|
||||||
+makeExample('architecture/dart/lib/hero_detail_component.html', 'ng-model', 'lib/hero_detail_component.html (excerpt)')(format=".")
|
|
||||||
:marked
|
|
||||||
In two-way binding, a data property value flows to the input box from the component as with property binding.
|
|
||||||
The user's changes also flow back to the component, resetting the property to the latest value,
|
|
||||||
as with event binding.
|
|
||||||
|
|
||||||
Angular processes *all* data bindings once per JavaScript event cycle,
|
|
||||||
depth-first from the root of the application component tree.
|
|
||||||
<!-- PENDING: clarify what "depth-first from the root" really means,
|
|
||||||
or reassure that they'll learn it soon. -->
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
|
|
||||||
:marked
|
|
||||||
We don't know all the details yet,
|
|
||||||
but it's clear from these examples that data binding plays an important role in communication
|
|
||||||
between a template and its component.
|
|
||||||
<br clear="all">
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
|
|
||||||
:marked
|
|
||||||
Data binding is also important for communication between parent and child components.
|
|
||||||
<br clear="all">
|
|
||||||
|
|
||||||
.l-main-section
|
|
||||||
<a id="directive"></a>
|
|
||||||
:marked
|
|
||||||
## Directives
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px" )
|
|
||||||
:marked
|
|
||||||
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM
|
|
||||||
according to the instructions given by **directives**.
|
|
||||||
|
|
||||||
A directive is a class with directive metadata. In Dart we apply the `@Directive` annotation
|
|
||||||
to attach metadata to the class.
|
|
||||||
<br clear="all">
|
|
||||||
:marked
|
|
||||||
We already met one form of directive: the component. A component is a *directive-with-a-template*;
|
|
||||||
a `@Component` annotation is actually a `@Directive` annotation extended with template-oriented features.
|
|
||||||
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
:marked
|
||||||
While **a component is technically a directive**,
|
Angular ships as a collection of libraries within the
|
||||||
components are so distinctive and central to Angular applications that we chose
|
[**angular2**](https://pub.dartlang.org/packages/angular2) package.
|
||||||
to separate components from directives in this architectural overview.
|
|
||||||
:marked
|
|
||||||
Two *other* kinds of directives exist: _structural_ and _attribute_ directives.
|
|
||||||
|
|
||||||
They tend to appear within an element tag like attributes,
|
block angular-imports
|
||||||
sometimes by name but more often as the target of an assignment or a binding.
|
+makeExcerpt('app/app.component.ts', 'import')
|
||||||
|
|
||||||
**Structural** directives alter layout by adding, removing, and replacing elements in DOM.
|
block ts-decorator
|
||||||
|
|
||||||
Our [example](#template) template uses two built-in structural directives:
|
|
||||||
+makeExample('architecture/dart/lib/hero_list_component_1.html', 'structural')(format=".")
|
|
||||||
:marked
|
|
||||||
* [`*ngFor`](displaying-data.html#ng-for) tells Angular to stamp out one `<div>` per hero in the `heroes` list.
|
|
||||||
* [`*ngIf`](displaying-data.html#ng-if) includes the `HeroDetail` component only if a selected hero exists.
|
|
||||||
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
:marked
|
||||||
In Dart, **the only value that is true is the boolean value `true`**; all
|
Annotations often have configuration parameters.
|
||||||
other values are false. JavaScript and TypeScript, in contrast, treat values
|
The `@Component` annotation takes parameters to provide the
|
||||||
such as 1 and most non-null objects as true. For this reason, the JavaScript
|
information Angular needs to create and present the component and its view.
|
||||||
and TypeScript versions of this app can use just `selectedHero` as the value
|
|
||||||
of the `*ngIf` expression. The Dart version must use a boolean operator such
|
|
||||||
as `!=` instead.
|
|
||||||
|
|
||||||
:marked
|
Here are a few of the possible `@Component` parameters:
|
||||||
**Attribute** directives alter the appearance or behavior of an existing element.
|
|
||||||
In templates they look like regular HTML attributes, hence the name.
|
|
||||||
|
|
||||||
The `ngModel` directive, which implements two-way data binding, is
|
block dart-bool
|
||||||
an example of an attribute directive. `ngModel` modifies the behavior of
|
.callout.is-helpful
|
||||||
an existing element (typically an `<input>`)
|
header Dart difference: Only true is true
|
||||||
by setting its display value property and responding to change events.
|
:marked
|
||||||
|
In Dart, **the only value that is true is the boolean value `true`**; all
|
||||||
+makeExample('architecture/dart/lib/hero_detail_component.html', 'ng-model')(format=".")
|
other values are false. JavaScript and TypeScript, in contrast, treat values
|
||||||
:marked
|
such as 1 and most non-null objects as true. For this reason, the JavaScript
|
||||||
Angular ships with a small number of other directives that either alter the layout structure
|
and TypeScript versions of this app can use just `selectedHero` as the value
|
||||||
(for example, `ngSwitch`) <!-- TODO: link to template-syntax.html#ng-switch -->
|
of the `*ngIf` expression. The Dart version must use a boolean operator such
|
||||||
or modify aspects of DOM elements and components
|
as `!=` instead.
|
||||||
(for example, `ngStyle` and `ngClass`).
|
|
||||||
<!-- PENDING: link to template-syntax.html#ng-style template-syntax.html#ng-class-->
|
|
||||||
|
|
||||||
Of course, we can also write our own directives. Components such as
|
|
||||||
`HeroListComponent` are one kind of custom directive.
|
|
||||||
<!-- PENDING: link to where to learn more about other kinds! -->
|
|
||||||
|
|
||||||
.l-main-section
|
|
||||||
<a id="service"></a>
|
|
||||||
:marked
|
|
||||||
## Services
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px" )
|
|
||||||
:marked
|
|
||||||
_Services_ is a broad category encompassing any value, function, or feature that our application needs.
|
|
||||||
|
|
||||||
Almost anything can be a service.
|
|
||||||
A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.
|
|
||||||
<br clear="all">
|
|
||||||
:marked
|
|
||||||
Examples include:
|
|
||||||
* logging service
|
|
||||||
* data service
|
|
||||||
* message bus
|
|
||||||
* tax calculator
|
|
||||||
* application configuration
|
|
||||||
|
|
||||||
There is nothing specifically _Angular_ about services. Angular itself has no definition of a service.
|
|
||||||
There is no service base class, and no place to register a service.
|
|
||||||
|
|
||||||
Yet services are fundamental to any Angular application. Our components are big consumers of services.
|
|
||||||
|
|
||||||
We prefer our component classes lean. Our components don't fetch data from the server,
|
|
||||||
they don't validate user input, and they don't log directly to console. They delegate such tasks to services.
|
|
||||||
|
|
||||||
A component's job is to enable the user experience and nothing more. It mediates between the view (rendered by the template)
|
|
||||||
and the application logic (which often includes some notion of a _model_).
|
|
||||||
A good component presents properties and methods for data binding.
|
|
||||||
It delegates everything nontrivial to services.
|
|
||||||
|
|
||||||
Angular doesn't *enforce* these principles.
|
|
||||||
It won't complain if we write a "kitchen sink" component with 3000 lines.
|
|
||||||
|
|
||||||
Angular does help us *follow* these principles by making it easy to factor our
|
|
||||||
application logic into services and make those services available to components through *dependency injection*.
|
|
||||||
|
|
||||||
.l-main-section
|
|
||||||
<a id="dependency-injection"></a>
|
|
||||||
:marked
|
|
||||||
## Dependency injection
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px" )
|
|
||||||
:marked
|
|
||||||
Dependency injection is a way to supply a new instance of a class
|
|
||||||
with the fully-formed dependencies it requires. Most dependencies are services.
|
|
||||||
Angular uses dependency injection to provide new components with the services they need.
|
|
||||||
<br clear="all">
|
|
||||||
:marked
|
|
||||||
Angular can tell which services a component needs by looking at the types of its constructor parameters.
|
|
||||||
For example, the constructor of our `HeroListComponent` needs a `HeroService`:
|
|
||||||
+makeExample('architecture/dart/lib/hero_list_component.dart', 'ctor', 'lib/hero_list_component.dart (excerpt)')(format='.')
|
|
||||||
:marked
|
|
||||||
When Angular creates a component, it first asks an **injector** for
|
|
||||||
the services that the component requires.
|
|
||||||
|
|
||||||
An injector maintains a container of service instances that it has previously created.
|
|
||||||
If a requested service instance is not in the container, the injector makes one and adds it to the container
|
|
||||||
before returning the service to Angular.
|
|
||||||
When all requested services have been resolved and returned,
|
|
||||||
Angular can call the component's constructor with those services as arguments.
|
|
||||||
This is what we mean by *dependency injection*.
|
|
||||||
|
|
||||||
The process of `HeroService` injection looks a bit like this:
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/injector-injects.png" alt="Service" )
|
|
||||||
:marked
|
|
||||||
If the injector doesn't have a `HeroService`, how does it know how to make one?
|
|
||||||
|
|
||||||
In brief, we must have previously registered a **provider** of the `HeroService` with the injector.
|
|
||||||
A provider is something that can create or return a service, typically the service class itself.
|
|
||||||
|
|
||||||
We can register providers at any level of the application component tree.
|
|
||||||
We often do so at the root when we bootstrap the application so that
|
|
||||||
the same instance of a service is available everywhere.
|
|
||||||
+makeExample('architecture/dart/web/main.dart', 'bootstrap', 'web/main.dart (excerpt)')(format='.')
|
|
||||||
:marked
|
|
||||||
Alternatively, we might register at a component level:
|
|
||||||
+makeExample('architecture/dart/lib/hero_list_component.dart', 'providers', 'lib/hero_list_component.dart (excerpt)')(format='.')
|
|
||||||
:marked
|
|
||||||
Registering at a component level means we get a new instance of the
|
|
||||||
service with each new instance of that component.
|
|
||||||
|
|
||||||
<!-- We've vastly oversimplified dependency injection for this overview.
|
|
||||||
The full story is in the [Dependency Injection](dependency-injection.html) chapter. -->
|
|
||||||
|
|
||||||
Points to remember about dependency injection:
|
|
||||||
|
|
||||||
* Dependency injection is wired into the Angular framework and used everywhere.
|
|
||||||
|
|
||||||
* The *injector* is the main mechanism.
|
|
||||||
* An injector maintains a *container* of service instances that it created.
|
|
||||||
* An injector can create a new service instance from a *provider*.
|
|
||||||
|
|
||||||
* A *provider* is a recipe for creating a service.
|
|
||||||
|
|
||||||
* We register *providers* with injectors.
|
|
||||||
|
|
||||||
<a id="other-stuff"></a>
|
|
||||||
.l-main-section
|
|
||||||
:marked
|
|
||||||
## Other stuff
|
|
||||||
|
|
||||||
We've learned just a bit about the seven main building blocks of an Angular application:
|
|
||||||
1. [Components](#component)
|
|
||||||
1. [Templates](#template)
|
|
||||||
1. [Metadata](#metadata)
|
|
||||||
1. [Data binding](#data-binding)
|
|
||||||
1. [Directives](#directive)
|
|
||||||
1. [Services](#service)
|
|
||||||
1. [Dependency injection](#dependency-injection)
|
|
||||||
|
|
||||||
That's a foundation for everything else in an Angular application,
|
|
||||||
and it's more than enough to get going.
|
|
||||||
But it doesn't include everything we'll need or want to know.
|
|
||||||
|
|
||||||
Here is a brief, alphabetical list of other important Angular features and services.
|
|
||||||
Most of them are covered in this Developers Guide (or soon will be).
|
|
||||||
|
|
||||||
>**Animations:** A forthcoming animation library makes it easy for developers to animate component behavior
|
|
||||||
without deep knowledge of animation techniques or CSS.
|
|
||||||
|
|
||||||
>**Bootstrap:** A method to configure and launch the root application component.
|
|
||||||
|
|
||||||
>**Change detection:** Learn how Angular decides that a component property value has changed and
|
|
||||||
when to update the screen.
|
|
||||||
Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies.
|
|
||||||
|
|
||||||
>**Component router:** With the component Router service, users can navigate a multi-screen application
|
|
||||||
in a familiar web browsing style using URLs.
|
|
||||||
|
|
||||||
>**Events:** The DOM raises events. So can components and services. Angular offers mechanisms for
|
|
||||||
publishing and subscribing to events including an implementation of the [RxJS Observable](https://github.com/zenparsing/es-observable) proposal.
|
|
||||||
|
|
||||||
>**[Forms](forms.html):** Support complex data entry scenarios with HTML-based validation and dirty checking.
|
|
||||||
|
|
||||||
>**HTTP:** Communicate with a server to get data, save data, and invoke server-side actions with this Angular HTTP client.
|
|
||||||
|
|
||||||
>**Lifecycle hooks:** We can tap into key moments in the lifetime of a component, from its creation to its destruction,
|
|
||||||
by implementing the lifecycle hook interfaces.
|
|
||||||
|
|
||||||
>**Pipes:** Services that transform values for display.
|
|
||||||
We can put pipes in our templates to improve the user experience. Consider
|
|
||||||
this `currency` pipe expression:
|
|
||||||
<div style="margin-left:40px">
|
|
||||||
code-example(language="javascript" linenumbers=".").
|
|
||||||
price | currency:'USD':true'
|
|
||||||
</div>
|
|
||||||
:marked
|
|
||||||
>It displays a price of "42.33" as `$42.33`.
|
|
||||||
|
|
||||||
>**Testing:** Angular provides a
|
|
||||||
[testing library](https://pub.dartlang.org/packages/angular2_testing)
|
|
||||||
to run unit tests on our application parts as they interact with the Angular framework.
|
|
||||||
|
@ -1,44 +1,51 @@
|
|||||||
include ../_util-fns
|
block includes
|
||||||
|
include ../_util-fns
|
||||||
|
- var _library_module = 'library module'
|
||||||
|
- var _at_angular = '@angular'
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Angular 2 is a framework to help us build client applications in HTML and JavaScript.
|
Angular 2 is a framework to help us build client applications in HTML and
|
||||||
|
either JavaScript or a language (like Dart or TypeScript) that compiles to JavaScript.
|
||||||
|
|
||||||
The framework consists of several cooperating libraries, some of them core and some optional.
|
block angular-parts
|
||||||
|
:marked
|
||||||
|
The framework consists of several cooperating libraries, some of them core and some optional.
|
||||||
|
|
||||||
We write applications by composing HTML *templates* with Angularized-markup,
|
:marked
|
||||||
|
With Angular, we write applications by composing HTML *templates* with Angularized-markup,
|
||||||
writing *component* classes to manage those templates, adding application logic in *services*,
|
writing *component* classes to manage those templates, adding application logic in *services*,
|
||||||
and handing the top root component to Angular's *bootstrapper*.
|
and handing the top root component to Angular's *bootstrapper*.
|
||||||
|
|
||||||
Angular takes over, presenting our application content in a browser and responding to user interactions
|
Angular takes over, presenting our application content in a browser and
|
||||||
according to the instructions we provided.
|
responding to user interactions according to the instructions we provided.
|
||||||
|
|
||||||
<!-- figure img(src="/resources/images/devguide/architecture/airplane.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) -->
|
Of course there is more to it than this.
|
||||||
:marked
|
We'll learn the details when we dive into the guide chapters.
|
||||||
Of course there is more to it than this. We'll learn the details when we dive into the guide chapters.
|
|
||||||
Let's get the big picture first.
|
Let's get the big picture first.
|
||||||
|
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700")
|
img(src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
The architecture diagram identifies the eight main building blocks of an Angular 2 application:
|
The architecture diagram identifies the eight main building blocks of an Angular 2 application:
|
||||||
1. [Module](#module)
|
|
||||||
1. [Component](#component)
|
|
||||||
1. [Template](#template)
|
|
||||||
1. [Metadata](#metadata)
|
|
||||||
1. [Data Binding](#data-binding)
|
|
||||||
1. [Directive](#directive)
|
|
||||||
1. [Service](#service)
|
|
||||||
1. [Dependency Injection](#dependency-injection)
|
|
||||||
|
|
||||||
Learn these eight and we're on our way.
|
1. [Modules](#modules)
|
||||||
|
1. [Components](#components)
|
||||||
|
1. [Templates](#templates)
|
||||||
|
1. [Metadata](#metadata)
|
||||||
|
1. [Data binding](#data-binding)
|
||||||
|
1. [Directives](#directives)
|
||||||
|
1. [Services](#services)
|
||||||
|
1. [Dependency injection](#dependency-injection)
|
||||||
|
|
||||||
|
Learn these, and we're on our way.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
p The code referenced in this chapter is available as a #[+liveExampleLink2()].
|
||||||
The code referenced in this chapter is available as a [live example](/resources/live-examples/architecture/ts/plnkr.html).
|
|
||||||
<a id="module"></a>
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## The Module
|
## Modules
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
@ -49,134 +56,158 @@ figure
|
|||||||
A typical module is a cohesive block of code dedicated to a single purpose.
|
A typical module is a cohesive block of code dedicated to a single purpose.
|
||||||
A module **exports** something of value in that code, typically one thing such as a class.
|
A module **exports** something of value in that code, typically one thing such as a class.
|
||||||
<br clear="all"><br>
|
<br clear="all"><br>
|
||||||
.l-sub-section
|
|
||||||
:marked
|
|
||||||
### Modules are optional
|
|
||||||
We highly recommend modular design. TypeScript has great support for ES2015 module syntax and our chapters assume we're taking a modular
|
|
||||||
approach using that syntax. That's why we list *Module* among the basic building blocks.
|
|
||||||
|
|
||||||
Angular itself doesn't require a modular approach nor this particular syntax. Don't use it if you don't want it.
|
block modules-in-dart
|
||||||
Each chapter has plenty to offer after you steer clear of the `import` and `export` statements.
|
//- N/A
|
||||||
|
|
||||||
Find setup and organization clues in the JavaScript track (select it from the combo-box at the top of this page)
|
block modules-are-optional
|
||||||
which demonstrates Angular 2 development with plain old JavaScript and no module system.
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
### Modules are optional
|
||||||
|
We highly recommend modular design. TypeScript has great support for ES2015 module syntax and our chapters assume we're taking a modular
|
||||||
|
approach using that syntax. That's why we list *Module* among the basic building blocks.
|
||||||
|
|
||||||
|
Angular itself doesn't require a modular approach nor this particular syntax. Don't use it if you don't want it.
|
||||||
|
Each chapter has plenty to offer after you steer clear of the `import` and `export` statements.
|
||||||
|
|
||||||
|
Find setup and organization clues in the JavaScript track (select it from the combo-box at the top of this page)
|
||||||
|
which demonstrates Angular 2 development with plain old JavaScript and no module system.
|
||||||
|
|
||||||
|
- var _app_comp_filename = _docsFor == 'dart' ? 'app_component.dart' : 'app.component.ts';
|
||||||
:marked
|
:marked
|
||||||
Perhaps the first module we meet is a module that exports a *component* class.
|
Perhaps the first module we meet is a module that exports a *component* class.
|
||||||
The component is one of the basic Angular blocks, we write a lot of them,
|
The component is one of the basic Angular blocks, we write a lot of them,
|
||||||
and we'll talk about components in the next segment. For the moment it is enough to know that a
|
and we'll talk about components in the next segment. For the moment it is enough to know that a
|
||||||
component class is the kind of thing we'd export from a module.
|
component class is the kind of thing we'd export from a module.
|
||||||
|
|
||||||
Most applications have an `AppComponent`. By convention, we'll find it in a file named `app.component.ts`.
|
Most applications have an `AppComponent`. By convention, we'll find it in a file named `!{_app_comp_filename}`.
|
||||||
Look inside such a file and we'll see an `export` statement like this one.
|
Look inside such a file and we'll see a declaration such as this one.
|
||||||
+makeExample('architecture/ts/app/app.component.ts', 'export', 'app/app.component.ts (excerpt)')(format=".")
|
|
||||||
:marked
|
+makeExcerpt('app/app.component.ts ()', 'export')
|
||||||
The `export` statement tells TypeScript that this is a module whose
|
|
||||||
`AppComponent` class is public and accessible to other modules of the application.
|
block export-qualifier
|
||||||
|
:marked
|
||||||
|
The `export` statement tells TypeScript that this is a module whose
|
||||||
|
`AppComponent` class is public and accessible to other modules of the application.
|
||||||
|
|
||||||
When we need a reference to the `AppComponent`, we **import** it like this:
|
|
||||||
+makeExample('architecture/ts/app/main.ts', 'import', 'app/main.ts (excerpt)')(format=".")
|
|
||||||
:marked
|
:marked
|
||||||
The `import` statement tells the system it can get an `AppComponent` from a module named `app.component`
|
When we need a reference to the `AppComponent`, we **import** it like this:
|
||||||
located in a neighboring file.
|
|
||||||
The **module name** (AKA module id) is often the same as the filename without its extension.
|
+makeExcerpt('app/main.ts', 'import')
|
||||||
### Library Modules
|
|
||||||
|
block ts-import
|
||||||
|
:marked
|
||||||
|
The `import` statement tells the system it can get an `AppComponent` from a module named `app.component`
|
||||||
|
located in a neighboring file.
|
||||||
|
The **module name** (AKA module id) is often the same as the filename without its extension.
|
||||||
|
|
||||||
|
:marked
|
||||||
|
### Libraries
|
||||||
|
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" )
|
||||||
|
|
||||||
|
block angular-library-modules
|
||||||
|
:marked
|
||||||
|
Some modules are _libraries_ of other modules.
|
||||||
|
Angular itself ships as a collection of library modules within several npm packages.
|
||||||
|
Their names begin with the `!{_at_angular}` prefix.
|
||||||
|
|
||||||
|
Each Angular library contains a [barrel](../glossary.html#barrel) module
|
||||||
|
that is actually a public façade over several logically-related private modules.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Some modules are libraries of other modules.
|
`!{_at_angular}/core` is the primary Angular library from which we get most of what we need.
|
||||||
|
|
||||||
Angular itself ships as a collection of library modules within several npm packages.
|
|
||||||
Their names begin with the `@angular` prefix.
|
|
||||||
Each Angular library contains a [barrel](../glossary.html#barrel) module
|
|
||||||
that is actually a public façade over several logically-related private modules.
|
|
||||||
|
|
||||||
The `@angular/core` library is the primary Angular library module from which we get most of what we need.
|
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
|
|
||||||
There are other important Angular library modules too such as `@angular/common`, `@angular/router`, and `@angular/http`.
|
There are other important Angular libraries too, such as `!{_at_angular}/common`, `!{_at_angular}/router`, and `!{_at_angular}/animate`.
|
||||||
|
We import what we need from an Angular !{_library_module}.
|
||||||
|
|
||||||
We import what we need from an Angular library module in much the same way.
|
block angular-imports
|
||||||
For example, we import the Angular **`Component` *function*** from the *@angular/core* module like this:
|
|
||||||
+makeExample('architecture/ts/app/app.component.ts', 'import')(format=".")
|
|
||||||
:marked
|
|
||||||
Compare that syntax to our previous import of `AppComponent`.
|
|
||||||
+makeExample('architecture/ts/app/main.ts', 'import')(format=".")
|
|
||||||
:marked
|
|
||||||
Notice the difference?
|
|
||||||
In the first case, when importing from an Angular library module,
|
|
||||||
the import statement refers to the bare module name, `@angular/core`, *without a path prefix*.
|
|
||||||
|
|
||||||
When we import from one of *our* own files, we prefix the module name with the file path.
|
|
||||||
In this example we specify a relative file path (./). That means the
|
|
||||||
source module is in the same folder (./) as the module importing it.
|
|
||||||
We could path up and around the application folder structure if the source module were somewhere else.
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
:marked
|
||||||
We import and export in the ECMAScript 2015 (ES2015) module syntax.
|
For example, we import the Angular **`Component` *function*** from `@angular/core` like this:
|
||||||
Learn more about that syntax [here](http://www.2ality.com/2014/09/es6-modules-final.html)
|
+makeExcerpt('app/app.component.ts', 'import')
|
||||||
and many other places on the web.
|
:marked
|
||||||
|
Compare that syntax to our previous import of `AppComponent`.
|
||||||
|
+makeExcerpt('app/main.ts', 'import')
|
||||||
|
|
||||||
The infrastructure *behind* module loading and importing is an important subject.
|
:marked
|
||||||
But it's a subject outside the scope of this introduction to Angular.
|
Notice the difference?
|
||||||
While we're focused on our application, *import* and *export*
|
In the first case, when importing from an Angular library module,
|
||||||
is about all we need to know.
|
the import statement refers to the bare module name, `@angular/core`, *without a path prefix*.
|
||||||
|
|
||||||
|
When we import from one of *our* own files, we prefix the module name with the file path.
|
||||||
|
In this example we specify a relative file path (`./`). That means the
|
||||||
|
source module is in the same folder (`./`) as the module importing it.
|
||||||
|
We could path up and around the application folder structure if the source module were somewhere else.
|
||||||
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
We import and export in the ECMAScript 2015 (ES2015) module syntax.
|
||||||
|
Learn more about that syntax [here](http://www.2ality.com/2014/09/es6-modules-final.html)
|
||||||
|
and many other places on the web.
|
||||||
|
|
||||||
|
The infrastructure *behind* module loading and importing is an important subject.
|
||||||
|
But it's a subject outside the scope of this introduction to Angular.
|
||||||
|
While we're focused on our application, *import* and *export*
|
||||||
|
is about all we need to know.
|
||||||
|
|
||||||
|
- var _export = _docsFor == 'dart' ? 'publicly declare' : 'export';
|
||||||
|
- var _declare = _docsFor == 'dart' ? 'declare' : 'export';
|
||||||
:marked
|
:marked
|
||||||
The key take-aways are:
|
The key take-aways are:
|
||||||
* Angular apps are composed of modules.
|
* Angular apps are composed of modules.
|
||||||
* Modules export things — classes, function, values — that other modules import.
|
* Modules !{_export} things — classes, function, values — that other modules import.
|
||||||
* We prefer to write our application as a collection of modules, each module exporting one thing.
|
* We prefer to write our application as a collection of modules, each module exporting one thing.
|
||||||
|
|
||||||
The first module we write will most likely export a component.
|
The first module we write will most likely !{_declare} a component.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="component"></a>
|
|
||||||
:marked
|
:marked
|
||||||
## The Component
|
## Components
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
|
||||||
A **Component** controls a patch of screen real estate that we could call a *view*.
|
|
||||||
The shell at the application root with navigation links, that list of heroes, the hero editor ...
|
|
||||||
they're all views controlled by Components.
|
|
||||||
|
|
||||||
We define a Component's application logic - what it does to support the view - inside a class.
|
:marked
|
||||||
|
A **component** controls a patch of screen real estate that we could call a *view*.
|
||||||
|
The shell at the application root with navigation links, a list of heroes, a hero editor ...
|
||||||
|
they're all views controlled by components.
|
||||||
|
|
||||||
|
We define a component's application logic — what it does to support the view — inside a class.
|
||||||
The class interacts with the view through an API of properties and methods.
|
The class interacts with the view through an API of properties and methods.
|
||||||
|
|
||||||
<a id="component-code"></a>
|
<a id="component-code"></a>
|
||||||
A `HeroListComponent`, for example, might have a `heroes` property that returns an array of heroes
|
A `HeroListComponent`, for example, might have a `heroes` property that returns !{_an} !{_array} of heroes
|
||||||
that it acquired from a service.
|
that it acquired from a service.
|
||||||
It might have a `selectHero()` method that sets a `selectedHero` property when the user clicks on a hero from that list.
|
It might have a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||||
It might be a class like this:
|
The component might be a class like this:
|
||||||
|
|
||||||
+makeExample('architecture/ts/app/hero-list.component.ts', 'class', 'app/hero-list.component.ts')
|
+makeExcerpt('app/hero-list.component.ts', 'class')
|
||||||
:marked
|
:marked
|
||||||
Angular creates, updates, and destroys components as the user moves through the application.
|
Angular creates, updates, and destroys components as the user moves through the application.
|
||||||
The developer can take action at each moment in this lifecycle through optional [Lifecycle Hooks](lifecycle-hooks.html).
|
The developer can take action at each moment in this lifecycle through optional [lifecycle hooks](lifecycle-hooks.html), like `ngOnInit()` declared above.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We're not showing those hooks in this example
|
We may wonder who is calling the component's constructor? Who provides the service parameter?
|
||||||
but we are making a mental note to find out about them later.
|
|
||||||
|
|
||||||
We may wonder who is calling that constructor? Who provides the service parameter?
|
|
||||||
For the moment, have faith that Angular will call the constructor and deliver an
|
For the moment, have faith that Angular will call the constructor and deliver an
|
||||||
appropriate `HeroService` when we need it.
|
appropriate `HeroService` when we need it.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="template"></a>
|
|
||||||
:marked
|
:marked
|
||||||
## The Template
|
## Templates
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
We define a Component's view with its companion **template**. A template is a form of HTML
|
We define a component's view with its companion **template**. A template is a form of HTML
|
||||||
that tells Angular how to render the Component.
|
that tells Angular how to render the component.
|
||||||
|
|
||||||
A template looks like regular HTML much of the time ... and then it gets a bit strange. Here is a
|
A template looks like regular HTML much of the time ... and then it gets a bit strange. Here is a
|
||||||
template for our `HeroList` component.
|
template for our `HeroListComponent`:
|
||||||
+makeExample('architecture/ts/app/hero-list.component.html',null,'app/hero-list.component.html')
|
|
||||||
|
+makeExample('app/hero-list.component.html')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We recognize `<h2>` and `<div>`.
|
This template features typical HTML elements like `<h2>` and `<p>`.
|
||||||
But there's other markup that no one told us about in school.
|
But what are `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>`?
|
||||||
What are `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>`?
|
|
||||||
|
|
||||||
These are examples of Angular's [template syntax](template-syntax.html).
|
These are examples of Angular's [template syntax](template-syntax.html).
|
||||||
We will grow accustomed to that syntax and may even learn to love it.
|
We will grow accustomed to that syntax and may even learn to love it.
|
||||||
@ -193,85 +224,86 @@ figure
|
|||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
Notice how `<hero-detail>` rests comfortably among the HTML elements we already know.
|
Notice how `<hero-detail>` rests comfortably among native HTML elements.
|
||||||
We can mix ... and will mix ... our custom components with native HTML in the same layouts.
|
We can and _will_ mix our custom components with native HTML in the same layouts.
|
||||||
|
|
||||||
And in this manner we can and will compose complex component trees to build out our richly featured application.
|
In this manner we'll compose complex component trees to build out our richly featured application.
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="metadata"></a>
|
|
||||||
:marked
|
:marked
|
||||||
## Angular Metadata
|
## Metadata
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px" )
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
<p style="padding-top:10px">Metadata tells Angular how to process a class.</p>
|
<p style="padding-top:10px">Metadata tells Angular how to process a class.</p>
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
:marked
|
:marked
|
||||||
[Looking back](#component-code) at the `HeroListComponent`, we see that it's just a class.
|
[Looking back at the code](#component-code) for `HeroListComponent`, we see that it's just a class.
|
||||||
There is no evidence of a framework, no "Angular" in it at all.
|
There is no evidence of a framework, no "Angular" in it at all.
|
||||||
|
|
||||||
In fact, it really is *just a class*. It's not a component until we *tell Angular about it*.
|
In fact, it really is *just a class*. It's not a component until we *tell Angular about it*.
|
||||||
|
|
||||||
We tell Angular that `HeroListComponent` is a component by attaching **metadata** to the class.
|
We tell Angular that `HeroListComponent` is a component by attaching **metadata** to the class.
|
||||||
|
|
||||||
The easy way to attach metadata in TypeScript is with a **decorator**.
|
In !{_Lang}, we attach metadata by using !{_a} **!{_decorator}**.
|
||||||
Here's some metadata for `HeroListComponent`:
|
Here's some metadata for `HeroListComponent`:
|
||||||
+makeExample('architecture/ts/app/hero-list.component.ts', 'metadata', 'app/hero-list.component.ts (metadata)')
|
|
||||||
|
+makeExcerpt('app/hero-list.component.ts', 'metadata')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Here we see the `@Component` decorator which (no surprise) identifies the class
|
Here we see the `@Component` !{_decorator} which (no surprise) identifies the class
|
||||||
immediately below it as a Component class.
|
immediately below it as a component class.
|
||||||
|
|
||||||
A decorator is a function. Decorators often have a configuration parameter.
|
block ts-decorator
|
||||||
The `@Component` decorator takes a required configuration object with the
|
:marked
|
||||||
information Angular needs to create and present the component and its view.
|
A decorator is a function. Decorators often have a configuration parameter.
|
||||||
|
The `@Component` decorator takes a required configuration object with the
|
||||||
|
information Angular needs to create and present the component and its view.
|
||||||
|
|
||||||
Here we see a few of the possible `@Component` configuration options:
|
Here are a few of the possible `@Component` configuration options:
|
||||||
|
|
||||||
* `selector` - a css selector that tells Angular to create and insert an instance of this component
|
:marked
|
||||||
|
- `selector`: CSS selector that tells Angular to create and insert an instance of this component
|
||||||
where it finds a `<hero-list>` tag in *parent* HTML.
|
where it finds a `<hero-list>` tag in *parent* HTML.
|
||||||
If the template of the application shell (a Component) contained
|
For example, if an app's HTML contains `<hero-list></hero-list>`, then
|
||||||
<div style="margin-left:30px">
|
Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||||
code-example(language="html").
|
|
||||||
<hero-list></hero-list>
|
|
||||||
</div>
|
|
||||||
:marked
|
|
||||||
>Angular inserts an instance of the `HeroListComponent` view between those tags.
|
|
||||||
|
|
||||||
* `templateUrl` - the address of this component's template which we showed [above](#template).
|
- `templateUrl`: address of this component's template, which we showed [above](#templates).
|
||||||
|
|
||||||
* `directives` - an array of the Components or Directives that *this* template requires.
|
- `directives`: !{_array} of the components or directives that *this* template requires.
|
||||||
We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent`
|
We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent`
|
||||||
in the space indicated by `<hero-detail>` tags.
|
in the space indicated by `<hero-detail>` tags.
|
||||||
Angular will do so only if we mention the `HeroDetailComponent` in this `directives` array.
|
Angular will do so only if we mention the `HeroDetailComponent` in this `directives` !{_array}.
|
||||||
|
|
||||||
* `providers` - an array of **dependency injection providers** for services that the component requires.
|
- `providers`: !{_array} of **dependency injection providers** for services that the component requires.
|
||||||
This is one way to tell Angular that our component's constructor requires a `HeroService`
|
This is one way to tell Angular that our component's constructor requires a `HeroService`
|
||||||
so it can get the list of heroes to display. We'll get to dependency injection in a moment.
|
so it can get the list of heroes to display. We'll get to dependency injection later.
|
||||||
|
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px" )
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
The `@Component` function takes the configuration object and turns it into metadata that it attaches
|
Angular reads the metadata specified by the `@Component`
|
||||||
to the component class definition. Angular discovers this metadata at runtime and thus knows how to do "the right thing".
|
annotation. That's how Angular learns to do "the right thing".
|
||||||
|
|
||||||
The template, metadata, and component together describe the view.
|
The template, metadata, and component together describe a view.
|
||||||
|
|
||||||
We apply other metadata decorators in a similar fashion to guide Angular behavior.
|
We apply other metadata !{_decorator}s in a similar fashion to guide Angular behavior.
|
||||||
The `@Injectable`, `@Input`, `@Output`, `@RouterConfig` are a few of the more popular decorators
|
`@Injectable`, `@Input`, and `@Output` are a few of the more popular !{_decorator}s
|
||||||
we'll master as our Angular knowledge grows.
|
we'll master as our Angular knowledge grows.
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
:marked
|
:marked
|
||||||
The architectural take-away is that we must add metadata to our code
|
The architectural takeaway is that we must add metadata to our code
|
||||||
so that Angular knows what to do.
|
so that Angular knows what to do.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="data-binding"></a>
|
|
||||||
:marked
|
:marked
|
||||||
## Data Binding
|
## Data binding
|
||||||
Without a framework, we would be responsible for pushing data values into the HTML controls and turning user responses
|
Without a framework, we would be responsible for pushing data values into the HTML controls and turning user responses
|
||||||
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone and a nightmare to
|
into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to
|
||||||
read as the experienced jQuery programmer can attest.
|
read as any experienced jQuery programmer can attest.
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px" )
|
img(src="/resources/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px" )
|
||||||
:marked
|
:marked
|
||||||
@ -279,108 +311,118 @@ figure
|
|||||||
a mechanism for coordinating parts of a template with parts of a component.
|
a mechanism for coordinating parts of a template with parts of a component.
|
||||||
We add binding markup to the template HTML to tell Angular how to connect both sides.
|
We add binding markup to the template HTML to tell Angular how to connect both sides.
|
||||||
|
|
||||||
There are four forms of data binding syntax. Each form has a direction - to the DOM, from the DOM, or in both directions -
|
There are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions —
|
||||||
as indicated by the arrows in the diagram.
|
as indicated by the arrows in the diagram.
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
:marked
|
:marked
|
||||||
We saw three forms of data binding in our [example](#template) template:
|
We saw three forms of data binding in our [example](#templates) template:
|
||||||
+makeExample('architecture/ts/app/hero-list.component.1.html', 'binding', 'app/hero-list.component.html (excerpt)')(format=".")
|
|
||||||
|
+makeExcerpt('app/hero-list.component.1.html', 'binding')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
* The `{{hero.name}}` [*interpolation*](displaying-data.html#interpolation)
|
* The `{{hero.name}}` [*interpolation*](displaying-data.html#interpolation)
|
||||||
displays the component's `hero.name` property value within the `<div>` tags.
|
displays the component's `hero.name` property value within the `<li>` tags.
|
||||||
|
|
||||||
* The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the `selectedHero` from
|
* The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the value of `selectedHero` from
|
||||||
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
|
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
|
||||||
|
|
||||||
* The `(click)` [*event binding*](user-input.html#click) calls the Component's `selectHero` method when the user clicks
|
* The `(click)` [*event binding*](user-input.html#click) calls the component's `selectHero` method when the user clicks a hero's name.
|
||||||
on a hero's name
|
|
||||||
|
|
||||||
**Two-way data binding** is an important fourth form
|
**Two-way data binding** is an important fourth form
|
||||||
that combines property and event binding in a single notation using the `ngModel` directive.
|
that combines property and event binding in a single notation, using the `ngModel` directive.
|
||||||
We didn't have a two-way binding in the `HeroListComponent` template;
|
We didn't have a two-way binding in the `HeroListComponent` template;
|
||||||
here's an example from the `HeroDetailComponent` template (not shown):
|
here's an example from the `HeroDetailComponent` template:
|
||||||
|
|
||||||
|
+makeExcerpt('app/hero-detail.component.html', 'ngModel')
|
||||||
|
|
||||||
+makeExample('architecture/ts/app/hero-detail.component.html', 'ngModel')(format=".")
|
|
||||||
:marked
|
:marked
|
||||||
In two-way binding, a data property value flows to the input box from the component as with property binding.
|
In two-way binding, a data property value flows to the input box from the component as with property binding.
|
||||||
The user's changes also flow back to the component, resetting the property to the latest value,
|
The user's changes also flow back to the component, resetting the property to the latest value,
|
||||||
as with event binding.
|
as with event binding.
|
||||||
|
|
||||||
Angular processes *all* data bindings once per JavaScript event cycle,
|
Angular processes *all* data bindings once per JavaScript event cycle,
|
||||||
depth-first from the root of the application component tree.
|
from the root of the application component tree down to the leaves.
|
||||||
|
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
We don't know all the details yet
|
We don't know all the details yet,
|
||||||
but it's clear from these examples that data binding plays an important role in communication
|
but it's clear from these examples that data binding plays an important role in communication
|
||||||
between a template and its component ...
|
between a template and its component.
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
... ***and*** between parent and child components
|
Data binding is also important for communication between parent and child components.
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="directive"></a>
|
|
||||||
:marked
|
:marked
|
||||||
## The Directive
|
## Directives
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
Our Angular templates are *dynamic*. When Angular renders them, it transforms the DOM
|
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM
|
||||||
according to the instructions given by a **directive**.
|
according to the instructions given by **directives**.
|
||||||
|
|
||||||
A directive is a class with directive metadata. In TypeScript we'd apply the `@Directive` decorator
|
A directive is a class with directive metadata. In !{_Lang} we apply the `@Directive` !{_decorator}
|
||||||
to attach metadata to the class.
|
to attach metadata to the class.
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
:marked
|
:marked
|
||||||
We already met one form of directive: the component. A component is a *directive-with-a-template*
|
We already met one form of directive: the component. A component is a *directive-with-a-template*;
|
||||||
and the `@Component` decorator is actually a `@Directive` decorator extended with template-oriented features.
|
a `@Component` !{_decorator} is actually a `@Directive` !{_decorator} extended with template-oriented features.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
While the **component is technically a directive**,
|
While **a component is technically a directive**,
|
||||||
it is so distinctive and central to Angular applications that we chose
|
components are so distinctive and central to Angular applications that we chose
|
||||||
to separate the component from the directive in our architectural overview.
|
to separate components from directives in this architectural overview.
|
||||||
:marked
|
:marked
|
||||||
There are two *other* kinds of directives as well that we call "structural" and "attribute" directives.
|
Two *other* kinds of directives exist: _structural_ and _attribute_ directives.
|
||||||
|
|
||||||
They tend to appear within an element tag like attributes,
|
They tend to appear within an element tag as attributes do,
|
||||||
sometimes by name but more often as the target of an assignment or a binding.
|
sometimes by name but more often as the target of an assignment or a binding.
|
||||||
|
|
||||||
**Structural** directives alter layout by adding, removing, and replacing elements in DOM.
|
**Structural** directives alter layout by adding, removing, and replacing elements in DOM.
|
||||||
|
|
||||||
We see two built-in structural directives at play in our [example](#template) template:
|
Our [example](#templates) template uses two built-in structural directives:
|
||||||
+makeExample('architecture/ts/app/hero-list.component.1.html', 'structural')(format=".")
|
|
||||||
|
+makeExcerpt('app/hero-list.component.1.html', 'structural')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
* [`*ngFor`](displaying-data.html#ngFor) tells Angular to stamp out one `<div>` per hero in the `heroes` list.
|
* [`*ngFor`](displaying-data.html#ngFor) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
||||||
* [`*ngIf`](displaying-data.html#ngIf) includes the `HeroDetail` component only if a selected hero exists.
|
* [`*ngIf`](displaying-data.html#ngIf) includes the `HeroDetail` component only if a selected hero exists.
|
||||||
|
|
||||||
|
block dart-bool
|
||||||
|
//- N/A
|
||||||
|
|
||||||
|
:marked
|
||||||
**Attribute** directives alter the appearance or behavior of an existing element.
|
**Attribute** directives alter the appearance or behavior of an existing element.
|
||||||
In templates they look like regular HTML attributes, hence the name.
|
In templates they look like regular HTML attributes, hence the name.
|
||||||
|
|
||||||
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive.
|
The `ngModel` directive, which implements two-way data binding, is
|
||||||
+makeExample('architecture/ts/app/hero-detail.component.html', 'ngModel')(format=".")
|
an example of an attribute directive. `ngModel` modifies the behavior of
|
||||||
:marked
|
an existing element (typically an `<input>`)
|
||||||
It modifies the behavior of an existing element (typically an `<input>`)
|
|
||||||
by setting its display value property and responding to change events.
|
by setting its display value property and responding to change events.
|
||||||
|
|
||||||
Angular ships with a few other directives that either alter the layout structure
|
+makeExcerpt('app/hero-detail.component.html', 'ngModel')
|
||||||
(e.g. [ngSwitch](template-syntax.html#ngSwitch))
|
:marked
|
||||||
|
Angular ships with a small number of other directives that either alter the layout structure
|
||||||
|
(for example, [ngSwitch](template-syntax.html#ngSwitch))
|
||||||
or modify aspects of DOM elements and components
|
or modify aspects of DOM elements and components
|
||||||
(e.g. [ngStyle](template-syntax.html#ngStyle) and [ngClass](template-syntax.html#ngClass)).
|
(for example, [ngStyle](template-syntax.html#ngStyle) and [ngClass](template-syntax.html#ngClass)).
|
||||||
|
|
||||||
And of course we can write our own directives.
|
Of course, we can also write our own directives. Components such as
|
||||||
|
`HeroListComponent` are one kind of custom directive.
|
||||||
|
<!-- PENDING: link to where to learn more about other kinds! -->
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="service"></a>
|
|
||||||
:marked
|
:marked
|
||||||
## The Service
|
## Services
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
"Service" is a broad category encompassing any value, function or feature that our application needs.
|
_Service_ is a broad category encompassing any value, function, or feature that our application needs.
|
||||||
|
|
||||||
Almost anything can be a service.
|
Almost anything can be a service.
|
||||||
A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.
|
A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.
|
||||||
@ -393,27 +435,32 @@ figure
|
|||||||
* tax calculator
|
* tax calculator
|
||||||
* application configuration
|
* application configuration
|
||||||
|
|
||||||
There is nothing specifically *Angular* about services. Angular itself has no definition of a *service*.
|
There is nothing specifically _Angular_ about services. Angular itself has no definition of a service.
|
||||||
There is no *ServiceBase* class.
|
There is no service base class, and no place to register a service.
|
||||||
|
|
||||||
Yet services are fundamental to any Angular application.
|
Yet services are fundamental to any Angular application. Our components are big consumers of services.
|
||||||
|
|
||||||
Here's an example of a service class that logs to the browser console
|
Here's an example of a service class that logs to the browser console
|
||||||
+makeExample('architecture/ts/app/logger.service.ts', 'class', 'app/logger.service.ts (class only)')(format=".")
|
|
||||||
|
+makeExcerpt('app/logger.service.ts', 'class')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Here's a `HeroService` that fetches heroes and returns them in a resolved [promise](http://exploringjs.com/es6/ch_promises.html).
|
Here's a `HeroService` that fetches heroes and returns them in a resolved !{_PromiseLinked}.
|
||||||
The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work.
|
The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work.
|
||||||
+makeExample('architecture/ts/app/hero.service.ts', 'class', 'app/hero.service.ts (class only)')(format=".")
|
|
||||||
|
+makeExcerpt('app/hero.service.ts', 'class')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Services are everywhere.
|
Services are everywhere.
|
||||||
|
|
||||||
Our components are big consumers of services. They depend upon services to handle most chores.
|
We prefer our component classes lean. Our components don't fetch data from the server,
|
||||||
They don't fetch data from the server, they don't validate user input, they don't log directly to the console.
|
they don't validate user input, and they don't log directly to the console.
|
||||||
They delegate such tasks to services.
|
They delegate such tasks to services.
|
||||||
|
|
||||||
A component's job is to enable the user experience and nothing more. It mediates between the view (rendered by the template)
|
A component's job is to enable the user experience and nothing more. It mediates between the view (rendered by the template)
|
||||||
and the application logic (which often includes some notion of a "model"). A good component presents
|
and the application logic (which often includes some notion of a _model_).
|
||||||
properties and methods for data binding. It delegates everything non-trivial to services.
|
A good component presents properties and methods for data binding.
|
||||||
|
It delegates everything nontrivial to services.
|
||||||
|
|
||||||
Angular doesn't *enforce* these principles.
|
Angular doesn't *enforce* these principles.
|
||||||
It won't complain if we write a "kitchen sink" component with 3000 lines.
|
It won't complain if we write a "kitchen sink" component with 3000 lines.
|
||||||
@ -422,25 +469,26 @@ figure
|
|||||||
application logic into services and make those services available to components through *dependency injection*.
|
application logic into services and make those services available to components through *dependency injection*.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="dependency-injection"></a>
|
|
||||||
:marked
|
:marked
|
||||||
## Dependency Injection
|
## Dependency injection
|
||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px" )
|
img(src="/resources/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px" )
|
||||||
:marked
|
:marked
|
||||||
"Dependency Injection" is a way to supply a new instance of a class
|
_Dependency injection_ is a way to supply a new instance of a class
|
||||||
with the fully-formed dependencies it requires. Most dependencies are services.
|
with the fully-formed dependencies it requires. Most dependencies are services.
|
||||||
Angular uses dependency injection to provide new components with the services they need.
|
Angular uses dependency injection to provide new components with the services they need.
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
:marked
|
:marked
|
||||||
In TypeScript, Angular can tell which services a component needs by looking at the types of its constructor parameters.
|
Angular can tell which services a component needs by looking at the types of its constructor parameters.
|
||||||
For example, the constructor of our `HeroListComponent` needs the `HeroService`:
|
For example, the constructor of our `HeroListComponent` needs a `HeroService`:
|
||||||
+makeExample('architecture/ts/app/hero-list.component.ts', 'ctor', 'app/hero-list.component (constructor)')(format=".")
|
|
||||||
|
+makeExcerpt('app/hero-list.component.ts (constructor)', 'ctor')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
When Angular creates a component, it first asks an **Injector** for
|
When Angular creates a component, it first asks an **injector** for
|
||||||
the services that the component requires.
|
the services that the component requires.
|
||||||
|
|
||||||
An `Injector` maintains a container of service instances that it has previously created.
|
An injector maintains a container of service instances that it has previously created.
|
||||||
If a requested service instance is not in the container, the injector makes one and adds it to the container
|
If a requested service instance is not in the container, the injector makes one and adds it to the container
|
||||||
before returning the service to Angular.
|
before returning the service to Angular.
|
||||||
When all requested services have been resolved and returned,
|
When all requested services have been resolved and returned,
|
||||||
@ -451,92 +499,98 @@ figure
|
|||||||
figure
|
figure
|
||||||
img(src="/resources/images/devguide/architecture/injector-injects.png" alt="Service" )
|
img(src="/resources/images/devguide/architecture/injector-injects.png" alt="Service" )
|
||||||
:marked
|
:marked
|
||||||
If the `Injector` doesn't have a `HeroService`, how does it know how to make one?
|
If the injector doesn't have a `HeroService`, how does it know how to make one?
|
||||||
|
|
||||||
In brief, we must have previously registered a **provider** of the `HeroService` with the `Injector`.
|
In brief, we must have previously registered a **provider** of the `HeroService` with the injector.
|
||||||
A provider is something that can create or return a service, typically the service class itself.
|
A provider is something that can create or return a service, typically the service class itself.
|
||||||
|
|
||||||
We can register providers at any level of the application component tree.
|
We can register providers at any level of the application component tree.
|
||||||
We often do so at the root when we bootstrap the application so that
|
We often do so at the root when we bootstrap the application so that
|
||||||
the same instance of a service is available everywhere.
|
the same instance of a service is available everywhere.
|
||||||
+makeExample('architecture/ts/app/main.ts', 'bootstrap','app/main.ts (excerpt)')(format=".")
|
|
||||||
|
+makeExcerpt('app/main.ts', 'bootstrap')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Alternatively, we might register at a component level ...
|
Alternatively, we might register at a component level, in the providers property of the `@Component` metadata:
|
||||||
+makeExample('architecture/ts/app/hero-list.component.ts', 'providers','app/hero-list.component.ts (excerpt)')(format=".")
|
|
||||||
|
+makeExcerpt('app/hero-list.component.ts', 'providers')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
... in which case we get a new instance of the
|
Registering at a component level means we get a new instance of the
|
||||||
service with each new instance of that component.
|
service with each new instance of that component.
|
||||||
|
|
||||||
We've vastly over-simplified dependency injection for this overview.
|
<!-- We've vastly oversimplified dependency injection for this overview.
|
||||||
We can learn the full story in the [Dependency Injection](dependency-injection.html) chapter.
|
The full story is in the [Dependency Injection](dependency-injection.html) chapter. -->
|
||||||
|
|
||||||
The points to remember are:
|
Points to remember about dependency injection:
|
||||||
* dependency injection is wired into the framework and used everywhere.<br><br>
|
|
||||||
* the `Injector` is the main mechanism.
|
|
||||||
* an injector maintains a *container* of service instances that it created.
|
|
||||||
* an injector can create a new service instance using a *provider*.
|
|
||||||
* a *provider* is a recipe for creating a service.
|
|
||||||
|
|
||||||
* we register *providers* with injectors.
|
* Dependency injection is wired into the Angular framework and used everywhere.
|
||||||
|
|
||||||
|
* The *injector* is the main mechanism.
|
||||||
|
* An injector maintains a *container* of service instances that it created.
|
||||||
|
* An injector can create a new service instance from a *provider*.
|
||||||
|
|
||||||
|
* A *provider* is a recipe for creating a service.
|
||||||
|
|
||||||
|
* We register *providers* with injectors.
|
||||||
|
|
||||||
<a id="other-stuff"></a>
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Wrap up
|
## Wrap up
|
||||||
We've learned just a bit about the eight main building blocks of an Angular application
|
|
||||||
|
|
||||||
1. [Module](#module)
|
We've learned just a bit about the eight main building blocks of an Angular application:
|
||||||
1. [Component](#component)
|
|
||||||
1. [Template](#template)
|
1. [Modules](#modules)
|
||||||
|
1. [Components](#components)
|
||||||
|
1. [Templates](#templates)
|
||||||
1. [Metadata](#metadata)
|
1. [Metadata](#metadata)
|
||||||
1. [Data Binding](#data-binding)
|
1. [Data binding](#data-binding)
|
||||||
1. [Directive](#directive)
|
1. [Directives](#directives)
|
||||||
1. [Service](#service)
|
1. [Services](#services)
|
||||||
1. [Dependency Injection](#dependency-injection)
|
1. [Dependency injection](#dependency-injection)
|
||||||
|
|
||||||
That's a foundation for everything else in an Angular application
|
That's a foundation for everything else in an Angular application,
|
||||||
and it's more than enough to get going.
|
and it's more than enough to get going.
|
||||||
But it doesn't include everything we'll need or want to know.
|
But it doesn't include everything we'll need or want to know.
|
||||||
|
|
||||||
<a id="other-stuff"></a>
|
|
||||||
.l-main-section
|
|
||||||
:marked
|
|
||||||
## The Other Stuff
|
|
||||||
|
|
||||||
Here is a brief, alphabetical list of other important Angular features and services.
|
Here is a brief, alphabetical list of other important Angular features and services.
|
||||||
Most of them are covered in this Developers Guide (or soon will be):
|
Most of them are covered in this Developers Guide (or soon will be).
|
||||||
|
|
||||||
>**Animations** - A forthcoming animation library makes it easy for developers to animate component behavior
|
> [**Animations**](animations.html): The animation library makes it easy for developers to animate component behavior
|
||||||
without deep knowledge of animation techniques or css.
|
without deep knowledge of animation techniques or CSS.
|
||||||
|
|
||||||
>**Bootstrap** - A method to configure and launch the root application component.
|
> **Bootstrap**: A method to configure and launch the root application component.
|
||||||
|
|
||||||
>**Change Detection** - Learn how Angular decides that a component property value has changed and
|
> **Change detection**: Learn how Angular decides that a component property value has changed and
|
||||||
when to update the screen.
|
when to update the screen.
|
||||||
Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies.
|
Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies.
|
||||||
|
|
||||||
>**[Component Router](router.html)** - With the Component Router service, users can navigate a multi-screen application
|
> **Component router**: With the component Router service, users can navigate a multi-screen application
|
||||||
in a familiar web browsing style using URLs.
|
in a familiar web browsing style using URLs.
|
||||||
|
|
||||||
>**Events** - The DOM raises events. So can components and services. Angular offers mechanisms for
|
> **Events**: The DOM raises events. So can components and services. Angular offers mechanisms for
|
||||||
publishing and subscribing to events including an implementation of the [RxJS Observable](https://github.com/zenparsing/es-observable) proposal.
|
publishing and subscribing to events.
|
||||||
|
|
||||||
>**[Forms](forms.html)** - Support complex data entry scenarios with HTML-based validation and dirty checking.
|
> [**Forms**](forms.html): Support complex data entry scenarios with HTML-based validation and dirty checking.
|
||||||
|
|
||||||
>**[HTTP](server-communication.html)** - Communicate with a server to get data, save data, and invoke server-side actions with this Angular HTTP client.
|
> [**HTTP**](server-communication.html): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
|
||||||
|
|
||||||
>**[Lifecycle Hooks](lifecycle-hooks.html)** - We can tap into key moments in the lifetime of a component, from its creation to its destruction,
|
> [**Lifecycle hooks**](lifecycle-hooks.html): We can tap into key moments in the lifetime of a component, from its creation to its destruction,
|
||||||
by implementing the "Lifecycle Hook" interfaces.
|
by implementing the lifecycle hook interfaces.
|
||||||
|
|
||||||
>**[Pipes](pipes.html)** - Services that transform values for display.
|
> [**Pipes**](pipes.html): Services that transform values for display.
|
||||||
We can put pipes in our templates to improve the user experience. For example,
|
We can put pipes in our templates to improve the user experience. Consider
|
||||||
this `currency` pipe expression,
|
this `currency` pipe expression:
|
||||||
<div style="margin-left:40px">
|
<div style="margin-left:40px">
|
||||||
code-example(language="javascript" linenumbers=".").
|
code-example().
|
||||||
price | currency:'USD':true
|
price | currency:'USD':true
|
||||||
</div>
|
</div>
|
||||||
:marked
|
:marked
|
||||||
>displays a price of "42.33" as `$42.33`.
|
> It displays a price of "42.33" as `$42.33`.
|
||||||
|
|
||||||
>**[Testing](testing.html)** - Angular provides a testing library for "unit testing" our application parts as they
|
> [**Router**](router.html): Navigate from page to page within the client
|
||||||
interact with the Angular framework.
|
application and never leave the browser.
|
||||||
|
|
||||||
|
> [**Testing**](testing.html): Angular provides a
|
||||||
|
[testing library](https://pub.dartlang.org/packages/angular2_testing)
|
||||||
|
to run unit tests on our application parts as they interact with the Angular framework.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user