docs(architecture): move diagram up; elab on svcs; add LC Hooks links
This commit is contained in:
parent
f697e01b76
commit
bf29846f3e
@ -2,11 +2,15 @@
|
|||||||
import {Component} from 'angular2/core';
|
import {Component} from 'angular2/core';
|
||||||
// #enddocregion import
|
// #enddocregion import
|
||||||
import {HeroListComponent} from './hero-list.component';
|
import {HeroListComponent} from './hero-list.component';
|
||||||
|
import {SalesTaxComponent} from './sales-tax.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
template: '<hero-list></hero-list>',
|
template: `
|
||||||
directives: [HeroListComponent]
|
<hero-list></hero-list>
|
||||||
|
<sales-tax></sales-tax>
|
||||||
|
`,
|
||||||
|
directives: [HeroListComponent, SalesTaxComponent]
|
||||||
})
|
})
|
||||||
// #docregion export
|
// #docregion export
|
||||||
export class AppComponent { }
|
export class AppComponent { }
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import {Injectable} from 'angular2/core';
|
import {Injectable, Type} from 'angular2/core';
|
||||||
import {Logger} from './logger.service';
|
import {Logger} from './logger.service';
|
||||||
import {Hero} from './hero';
|
import {Hero} from './hero';
|
||||||
|
|
||||||
|
const HEROES = [
|
||||||
|
new Hero('Windstorm', 'Weather mastery'),
|
||||||
|
new Hero('Mr. Nice', 'Killing them with kindness'),
|
||||||
|
new Hero('Magneta', 'Manipulates metalic objects')
|
||||||
|
];
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BackendService {
|
export class BackendService {
|
||||||
constructor(private _logger: Logger) {}
|
constructor(private _logger: Logger) {}
|
||||||
|
|
||||||
getAll<T>(type: {new(...args:any[]): any }) : any[]{
|
getAll(type:Type) : PromiseLike<any[]>{
|
||||||
if (type === Hero) {
|
if (type === Hero) {
|
||||||
// TODO get from the database and return as a promise
|
// TODO get from the database
|
||||||
return [
|
return Promise.resolve<Hero[]>(HEROES);
|
||||||
new Hero('Windstorm', 'Weather mastery'),
|
|
||||||
new Hero('Mr. Nice', 'Killing them with kindness'),
|
|
||||||
new Hero('Magneta', 'Manipulates metalic objects')];
|
|
||||||
}
|
}
|
||||||
let err = new Error('Cannot get object of this type');
|
let err = new Error('Cannot get object of this type');
|
||||||
this._logger.error(err);
|
this._logger.error(err);
|
||||||
|
@ -7,7 +7,5 @@ import {BackendService} from './backend.service';
|
|||||||
import {Logger} from './logger.service';
|
import {Logger} from './logger.service';
|
||||||
|
|
||||||
// #docregion bootstrap
|
// #docregion bootstrap
|
||||||
bootstrap(AppComponent, [
|
bootstrap(AppComponent, [BackendService, HeroService, Logger]);
|
||||||
BackendService, HeroService, Logger
|
|
||||||
]);
|
|
||||||
// #enddocregion bootstrap
|
// #enddocregion bootstrap
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
|
import {Component, OnInit} from 'angular2/core';
|
||||||
import {Component} from 'angular2/core';
|
import {Hero} from './hero';
|
||||||
import {Hero} from './hero';
|
|
||||||
import {HeroDetailComponent} from './hero-detail.component';
|
import {HeroDetailComponent} from './hero-detail.component';
|
||||||
import {HeroService} from './hero.service'
|
import {HeroService} from './hero.service';
|
||||||
|
|
||||||
// #docregion metadata
|
// #docregion metadata
|
||||||
// #docregion providers
|
// #docregion providers
|
||||||
@ -23,15 +22,18 @@ export class HeroesComponent { ... }
|
|||||||
// #enddocregion metadata, providers
|
// #enddocregion metadata, providers
|
||||||
*/
|
*/
|
||||||
// #docregion class
|
// #docregion class
|
||||||
export class HeroListComponent {
|
export class HeroListComponent implements OnInit {
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(service: HeroService) {
|
constructor(private _service: HeroService){ }
|
||||||
this.heroes = service.getHeroes();
|
|
||||||
}
|
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
heroes:Hero[];
|
heroes:Hero[];
|
||||||
selectedHero:Hero;
|
selectedHero: Hero;
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.heroes = this._service.getHeroes();
|
||||||
|
}
|
||||||
|
|
||||||
selectHero(hero: Hero) { this.selectedHero = hero; }
|
selectHero(hero: Hero) { this.selectedHero = hero; }
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
// #enddocregion class
|
||||||
|
@ -3,16 +3,23 @@ import {Hero} from './hero';
|
|||||||
import {BackendService} from './backend.service';
|
import {BackendService} from './backend.service';
|
||||||
import {Logger} from './logger.service';
|
import {Logger} from './logger.service';
|
||||||
|
|
||||||
// #docregion class
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
// #docregion class
|
||||||
export class HeroService {
|
export class HeroService {
|
||||||
constructor(private _backend: BackendService, private _logger:Logger){}
|
// #docregion ctor
|
||||||
|
constructor(
|
||||||
|
private _backend: BackendService,
|
||||||
|
private _logger: Logger) { }
|
||||||
|
// #enddocregion ctor
|
||||||
|
|
||||||
|
private _heroes:Hero[] = [];
|
||||||
|
|
||||||
getHeroes() {
|
getHeroes() {
|
||||||
// TODO return as a promise
|
this._backend.getAll(Hero).then( (heroes:Hero[]) => {
|
||||||
let heroes = <Hero[]> this._backend.getAll(Hero);
|
this._logger.log(`Fetched ${heroes.length} heroes.`);
|
||||||
this._logger.log(`Got ${heroes.length} heroes from the server.`);
|
this._heroes.push(...heroes); // fill cache
|
||||||
return heroes;
|
});
|
||||||
|
return this._heroes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion class
|
// #enddocregion class
|
@ -1,8 +1,11 @@
|
|||||||
|
// #docregion
|
||||||
import {Injectable} from 'angular2/core';
|
import {Injectable} from 'angular2/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
// #docregion class
|
||||||
export class Logger {
|
export class Logger {
|
||||||
log(msg: any) { console.log(msg); }
|
log(msg: any) { console.log(msg); }
|
||||||
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
|
@ -0,0 +1,41 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
|
import {Component} from 'angular2/core';
|
||||||
|
import {SalesTaxService} from './sales-tax.service';
|
||||||
|
import {TaxRateService} from './tax-rate.service';
|
||||||
|
|
||||||
|
// #docregion metadata
|
||||||
|
// #docregion providers
|
||||||
|
@Component({
|
||||||
|
// #enddocregion providers
|
||||||
|
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':true:'1.2-2' }}
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
// #docregion providers
|
||||||
|
providers: [SalesTaxService, TaxRateService]
|
||||||
|
})
|
||||||
|
// #enddocregion providers
|
||||||
|
// #enddocregion metadata
|
||||||
|
/*
|
||||||
|
// #docregion metadata, providers
|
||||||
|
export class SalesTaxComponent { ... }
|
||||||
|
// #enddocregion metadata, providers
|
||||||
|
*/
|
||||||
|
// #docregion class
|
||||||
|
export class SalesTaxComponent {
|
||||||
|
// #docregion ctor
|
||||||
|
constructor(private _salesTaxService: SalesTaxService) { }
|
||||||
|
// #enddocregion ctor
|
||||||
|
|
||||||
|
getTax(value:string | number){
|
||||||
|
return this._salesTaxService.getVAT(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion class
|
@ -0,0 +1,19 @@
|
|||||||
|
// #docregion
|
||||||
|
import {Injectable, Inject} from 'angular2/core';
|
||||||
|
import {TaxRateService} from './tax-rate.service';
|
||||||
|
|
||||||
|
// #docregion class
|
||||||
|
@Injectable()
|
||||||
|
export class SalesTaxService {
|
||||||
|
constructor(private _rateService: TaxRateService) { }
|
||||||
|
getVAT(value:string | number){
|
||||||
|
let amount:number;
|
||||||
|
if (typeof value === "string"){
|
||||||
|
amount = parseFloat(value);
|
||||||
|
} else {
|
||||||
|
amount = value;
|
||||||
|
}
|
||||||
|
return (amount || 0) * this._rateService.getRate('VAT');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion class
|
@ -0,0 +1,9 @@
|
|||||||
|
// #docregion
|
||||||
|
import {Injectable} from 'angular2/core';
|
||||||
|
|
||||||
|
// #docregion class
|
||||||
|
@Injectable()
|
||||||
|
export class TaxRateService {
|
||||||
|
getRate(rateName:string){return 0.10;} // always 10% everywhere
|
||||||
|
}
|
||||||
|
// #enddocregion class
|
@ -14,14 +14,13 @@ include ../../../../_includes/_util-fns
|
|||||||
|
|
||||||
<!-- figure img(src="/resources/images/devguide/architecture/airplane.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) -->
|
<!-- figure img(src="/resources/images/devguide/architecture/airplane.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) -->
|
||||||
:marked
|
:marked
|
||||||
Of course there is more to it than this.
|
Of course there is more to it than this. We'll learn the details when we dive into the guide chapters.
|
||||||
We're cruising at high altitude in this overview.
|
Let's get the big picture first.
|
||||||
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">
|
|
||||||
|
|
||||||
|
figure
|
||||||
|
img(src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700")
|
||||||
:marked
|
:marked
|
||||||
An Angular 2 application rests on eight main building blocks:
|
The architecture diagram identifies the eight main building blocks of an Angular 2 application:
|
||||||
1. [Module](#module)
|
1. [Module](#module)
|
||||||
1. [Component](#component)
|
1. [Component](#component)
|
||||||
1. [Template](#template)
|
1. [Template](#template)
|
||||||
@ -30,10 +29,7 @@ include ../../../../_includes/_util-fns
|
|||||||
1. [Service](#service)
|
1. [Service](#service)
|
||||||
1. [Directive](#directive)
|
1. [Directive](#directive)
|
||||||
1. [Dependency Injection](#dependency-injection)
|
1. [Dependency Injection](#dependency-injection)
|
||||||
|
|
||||||
figure
|
|
||||||
img(src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700")
|
|
||||||
:marked
|
|
||||||
Learn these eight and we're on our way.
|
Learn these eight and we're on our way.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
@ -53,6 +49,18 @@ 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.
|
||||||
|
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 combobox at the top of this page)
|
||||||
|
which demonstrates Angular 2 development with plain old JavaScript and no module system.
|
||||||
|
: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
|
||||||
@ -144,7 +152,7 @@ figure
|
|||||||
+makeExample('architecture/ts/app/hero-list.component.ts', 'class', 'app/hero-list.component.ts')
|
+makeExample('architecture/ts/app/hero-list.component.ts', 'class', 'app/hero-list.component.ts')
|
||||||
: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](#).
|
The developer can take action at each moment in this lifecycle through optional [Lifecycle Hooks](lifecycle-hooks.html).
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We're not showing those hooks in this example
|
We're not showing those hooks in this example
|
||||||
@ -233,7 +241,7 @@ code-example(language="html").
|
|||||||
:marked
|
:marked
|
||||||
>Angular inserts an instance of the `HeroListComponent` view between those tags.
|
>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).
|
* `templateUrl` - the address of this component's template which we showed [above](#template).
|
||||||
|
|
||||||
* `directives` - an array of the Components or Directives that *this* template requires.
|
* `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`
|
We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent`
|
||||||
@ -387,13 +395,23 @@ 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 service base class, no place to register a "service".
|
There is no *ServiceBase* class.
|
||||||
|
|
||||||
Yet services are fundamental to any Angular application. Our components are big consumers of services.
|
Yet services are fundamental to any Angular application.
|
||||||
|
|
||||||
|
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=".")
|
||||||
|
:marked
|
||||||
|
Here's a `HeroServce` that fetches heroes and returns them in a resolved [promise](http://www.html5rocks.com/en/tutorials/es6/promises/).
|
||||||
|
The `HeroService` depends on the `LoggerService` 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=".")
|
||||||
|
:marked
|
||||||
|
Services are everywhere.
|
||||||
|
|
||||||
We prefer our component classes lean. Our components don't fetch data from the server,
|
Our components are big consumers of services. They depend upon services to handle most chores.
|
||||||
they don't validate user input, they don't log directly to the console. They delegate such tasks to services.
|
They don't fetch data from the server, they don't validate user input, they don't log directly to the 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)
|
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"). A good component presents
|
||||||
@ -499,7 +517,7 @@ figure
|
|||||||
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** - With the Component Router service, users can navigate a multi-screen application
|
>**[Component Router](router.html)** - 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
|
||||||
@ -509,7 +527,7 @@ figure
|
|||||||
|
|
||||||
>**HTTP** - Communicate with a server to get data, save data, and invoke server-side actions with this Angular HTTP client.
|
>**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,
|
>**[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.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
:marked
|
:marked
|
||||||
# Angular 2 Glossary
|
# Angular 2 Glossary
|
||||||
|
|
||||||
Angular 2 has a vocabulary of its own.
|
Angular 2 has a vocabulary of its own.
|
||||||
Most Angular 2 terms are everyday English words
|
Most Angular 2 terms are everyday English words
|
||||||
with a specific meaning within the Angular system.
|
with a specific meaning within the Angular system.
|
||||||
@ -9,7 +9,7 @@
|
|||||||
and a few less familiar ones that have unusual or
|
and a few less familiar ones that have unusual or
|
||||||
unexpected definitions.
|
unexpected definitions.
|
||||||
|
|
||||||
[A](#A) [B](#B) [C](#C) [D](#D) [E](#E) [F](#F) [G](#G) [H](#H) [I](#I)
|
[A](#A) [B](#B) [C](#C) [D](#D) [E](#E) [F](#F) [G](#G) [H](#H) [I](#I)
|
||||||
[J](#J) [K](#K) [L](#L) [M](#M) [N](#N) [O](#O) [P](#P) [Q](#Q) [R](#R)
|
[J](#J) [K](#K) [L](#L) [M](#M) [N](#N) [O](#O) [P](#P) [Q](#Q) [R](#R)
|
||||||
[S](#S) [T](#T) [U](#U) [V](#V) [W](#W) [X](#X) [Y](#Y) [Z](#Z)
|
[S](#S) [T](#T) [U](#U) [V](#V) [W](#W) [X](#X) [Y](#Y) [Z](#Z)
|
||||||
.l-main-section
|
.l-main-section
|
||||||
@ -39,15 +39,15 @@
|
|||||||
:marked
|
:marked
|
||||||
A **barrel** is an Angular library module consisting of a logical grouping of single-purpose modules
|
A **barrel** is an Angular library module consisting of a logical grouping of single-purpose modules
|
||||||
such as `Component` and `Directive`.
|
such as `Component` and `Directive`.
|
||||||
|
|
||||||
Familiar barrels include `angular2/core`, `angular2/common`, `angular2/platform/browser`,
|
Familiar barrels include `angular2/core`, `angular2/common`, `angular2/platform/browser`,
|
||||||
`angular2/http`, and `angular2/router`.
|
`angular2/http`, and `angular2/router`.
|
||||||
|
|
||||||
Barrels are packaged and shipped as [**bundles**](#bundle) that
|
Barrels are packaged and shipped as [**bundles**](#bundle) that
|
||||||
we may load with script tags in our `index.html`.
|
we may load with script tags in our `index.html`.
|
||||||
|
|
||||||
The script, `angular2.dev.js`, is a bundle.
|
The script, `angular2.dev.js`, is a bundle.
|
||||||
|
|
||||||
Learn more in "[Modules, barrels and bundles](https://github.com/angular/angular/blob/master/modules/angular2/docs/bundles/overview.md)".
|
Learn more in "[Modules, barrels and bundles](https://github.com/angular/angular/blob/master/modules/angular2/docs/bundles/overview.md)".
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
@ -77,14 +77,14 @@
|
|||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Angular JavaScript libraries are shipped in **bundles** within an **npm package**
|
Angular JavaScript libraries are shipped in **bundles** within an **npm package**
|
||||||
such as [angular2](https://www.npmjs.com/package/angular2).
|
such as [angular2](https://www.npmjs.com/package/angular2).
|
||||||
|
|
||||||
The scripts `angular2.dev.js`, `http.js`, `router.js`, and `Rx.js` are
|
The scripts `angular2.dev.js`, `http.js`, `router.js`, and `Rx.js` are
|
||||||
familiar examples of bundles.
|
familiar examples of bundles.
|
||||||
|
|
||||||
A bundle contains one more more [**barrels**](#barrel)
|
A bundle contains one more more [**barrels**](#barrel)
|
||||||
and each barrel contains a collection of logically related [modules](#module)
|
and each barrel contains a collection of logically related [modules](#module)
|
||||||
|
|
||||||
Familiar barrels include `angular2/core`, `angular2/common`, `angular2/platform/browser`,
|
Familiar barrels include `angular2/core`, `angular2/common`, `angular2/platform/browser`,
|
||||||
`angular2/http`, `angular2/router`.
|
`angular2/http`, `angular2/router`.
|
||||||
|
|
||||||
@ -359,13 +359,13 @@
|
|||||||
:marked
|
:marked
|
||||||
[Directives](#directive) and [Components](#component) have a lifecycle
|
[Directives](#directive) and [Components](#component) have a lifecycle
|
||||||
managed by Angular as it creates, updates and destroys them.
|
managed by Angular as it creates, updates and destroys them.
|
||||||
|
|
||||||
Developers can tap into key moments in that lifecycle by implementing
|
Developers can tap into key moments in that lifecycle by implementing
|
||||||
one or more of the "Lifecycle Hook" interfaces.
|
one or more of the "Lifecycle Hook" interfaces.
|
||||||
|
|
||||||
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
||||||
For example, the `OnInit` interface has a hook method names `ngOnInit`.
|
For example, the `OnInit` interface has a hook method names `ngOnInit`.
|
||||||
|
|
||||||
Angular calls these hook methods in the following order:
|
Angular calls these hook methods in the following order:
|
||||||
* `ngOnChanges` - called when an [input](#input)/[output](#output) binding values change
|
* `ngOnChanges` - called when an [input](#input)/[output](#output) binding values change
|
||||||
* `ngOnInit` - after the first `ngOnChanges`
|
* `ngOnInit` - after the first `ngOnChanges`
|
||||||
@ -376,6 +376,7 @@
|
|||||||
* `ngAfterViewChecked` - after every check of a component's view(s)
|
* `ngAfterViewChecked` - after every check of a component's view(s)
|
||||||
* `ngOnDestroy` - just before the directive is destroyed.
|
* `ngOnDestroy` - just before the directive is destroyed.
|
||||||
|
|
||||||
|
Learn more in the [Lifecycle Hooks](lifecycle-hooks.html) chapter.
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="M"></a>
|
<a id="M"></a>
|
||||||
:marked
|
:marked
|
||||||
@ -388,29 +389,29 @@
|
|||||||
and the ones we acquire from others.
|
and the ones we acquire from others.
|
||||||
|
|
||||||
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.
|
||||||
A module that needs that thing, **imports** it.
|
A module that needs that thing, **imports** it.
|
||||||
|
|
||||||
The structure of Angular modules and the import/export syntax
|
The structure of Angular modules and the import/export syntax
|
||||||
is based on the [ES2015](#es2015) module standard
|
is based on the [ES2015](#es2015) module standard
|
||||||
described [here](http://www.2ality.com/2014/09/es6-modules-final.html).
|
described [here](http://www.2ality.com/2014/09/es6-modules-final.html).
|
||||||
|
|
||||||
An application that adheres to this standard requires a module loader to
|
An application that adheres to this standard requires a module loader to
|
||||||
load modules on request and resolve inter-module dependencies.
|
load modules on request and resolve inter-module dependencies.
|
||||||
Angular does not ship with a module loader and does not have a preference
|
Angular does not ship with a module loader and does not have a preference
|
||||||
for any particular 3rd party library (although most samples use SystemJS).
|
for any particular 3rd party library (although most samples use SystemJS).
|
||||||
Application developers may pick any module library that conforms to the standard
|
Application developers may pick any module library that conforms to the standard
|
||||||
|
|
||||||
Modules are typically named after the file in which the exported thing is defined.
|
Modules are typically named after the file in which the exported thing is defined.
|
||||||
The Angular [DatePipe](https://github.com/angular/angular/blob/master/modules/angular2/src/common/pipes/date_pipe.ts)
|
The Angular [DatePipe](https://github.com/angular/angular/blob/master/modules/angular2/src/common/pipes/date_pipe.ts)
|
||||||
class belongs to a feature module named `date_pipe` in the file `date_pipe.ts`.
|
class belongs to a feature module named `date_pipe` in the file `date_pipe.ts`.
|
||||||
|
|
||||||
Developers rarely access Angular feature modules directly.
|
Developers rarely access Angular feature modules directly.
|
||||||
We usually import them from public-facing **library modules**
|
We usually import them from public-facing **library modules**
|
||||||
called [**barrels**](#barrel). Barrels are groups of logically related modules.
|
called [**barrels**](#barrel). Barrels are groups of logically related modules.
|
||||||
The `angular2/core` barrel is a good example.
|
The `angular2/core` barrel is a good example.
|
||||||
|
|
||||||
Learn more in "[Modules, barrels and bundles](https://github.com/angular/angular/blob/master/modules/angular2/docs/bundles/overview.md)".
|
Learn more in "[Modules, barrels and bundles](https://github.com/angular/angular/blob/master/modules/angular2/docs/bundles/overview.md)".
|
||||||
|
|
||||||
<a id="N"></a>
|
<a id="N"></a>
|
||||||
@ -477,13 +478,13 @@
|
|||||||
## Routing Component
|
## Routing Component
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
A [Component](#component) with an attached router.
|
A [Component](#component) with an attached router.
|
||||||
|
|
||||||
In most cases, the component became attached to a [router](#router) by means
|
In most cases, the component became attached to a [router](#router) by means
|
||||||
of a `@RouterConfig` decorator that defined routes to views controlled by this component.
|
of a `@RouterConfig` decorator that defined routes to views controlled by this component.
|
||||||
|
|
||||||
The component's template has a `RouterOutlet` element where it can display views produced by the router.
|
The component's template has a `RouterOutlet` element where it can display views produced by the router.
|
||||||
|
|
||||||
It likely has anchor tags or buttons with `RouterLink` directives that users can click to navigate.
|
It likely has anchor tags or buttons with `RouterLink` directives that users can click to navigate.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,62 +3,62 @@ include ../../../../_includes/_util-fns
|
|||||||
:marked
|
:marked
|
||||||
One of the defining features of a single page application is its manipulation
|
One of the defining features of a single page application is its manipulation
|
||||||
of the DOM tree. Instead of serving a whole new page every time a user
|
of the DOM tree. Instead of serving a whole new page every time a user
|
||||||
navigates, whole sections of the DOM appear and disappear according
|
navigates, whole sections of the DOM appear and disappear according
|
||||||
to the application state. In this chapter we'll to look at how Angular
|
to the application state. In this chapter we'll to look at how Angular
|
||||||
manipulates the DOM and how we can do it ourselves in our own directives.
|
manipulates the DOM and how we can do it ourselves in our own directives.
|
||||||
|
|
||||||
In this chapter we will
|
In this chapter we will
|
||||||
- [learn what structural directives are](#definition)
|
- [learn what structural directives are](#definition)
|
||||||
- [study *ngIf*](#ng-if)
|
- [study *ngIf*](#ng-if)
|
||||||
- [discover the <template> element](#template)
|
- [discover the <template> element](#template)
|
||||||
- [understand the asterisk (\*) in **ngFor*](#asterisk)
|
- [understand the asterisk (\*) in **ngFor*](#asterisk)
|
||||||
- [write our own structural directive](#unless)
|
- [write our own structural directive](#unless)
|
||||||
|
|
||||||
[Live example](/resources/live-examples/structural-directives/ts/plnkr.html)
|
[Live example](/resources/live-examples/structural-directives/ts/plnkr.html)
|
||||||
|
|
||||||
<a id="definition"></a>
|
<a id="definition"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## What are structural directives?
|
## What are structural directives?
|
||||||
|
|
||||||
There are three kinds of Angular directives:
|
There are three kinds of Angular directives:
|
||||||
1. Components
|
1. Components
|
||||||
1. Attribute directives
|
1. Attribute directives
|
||||||
1. Structural directives
|
1. Structural directives
|
||||||
|
|
||||||
The *Component* is really a directive with a template.
|
The *Component* is really a directive with a template.
|
||||||
It's the most common of the three directives and we write lots of them as we build our application.
|
It's the most common of the three directives and we write lots of them as we build our application.
|
||||||
|
|
||||||
The [*Attribute* directive](attribute-directives.html) changes the appearance or behavior of an element.
|
The [*Attribute* directive](attribute-directives.html) changes the appearance or behavior of an element.
|
||||||
The built-in [NgStyle](template-syntax.html#ng-style) directive, for example,
|
The built-in [NgStyle](template-syntax.html#ng-style) directive, for example,
|
||||||
can change several element styles at the same time.
|
can change several element styles at the same time.
|
||||||
We can use it to render text bold, italic, and lime green by binding to a
|
We can use it to render text bold, italic, and lime green by binding to a
|
||||||
component property that requests such a sickening result.
|
component property that requests such a sickening result.
|
||||||
|
|
||||||
A *Structural* directive changes the DOM layout by adding and removing DOM elements.
|
A *Structural* directive changes the DOM layout by adding and removing DOM elements.
|
||||||
We've seen three of the built-in structural directives in other chapters: [ngIf](template-syntax.html#ngIf),
|
We've seen three of the built-in structural directives in other chapters: [ngIf](template-syntax.html#ngIf),
|
||||||
[ngSwitch](template-syntax.html#ngSwitch) and [ngFor](template-syntax.html#ngFor).
|
[ngSwitch](template-syntax.html#ngSwitch) and [ngFor](template-syntax.html#ngFor).
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'structural-directives')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'structural-directives')(format=".")
|
||||||
|
|
||||||
|
|
||||||
<a id="ng-if"></a>
|
<a id="ng-if"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## NgIf Case Study
|
## NgIf Case Study
|
||||||
|
|
||||||
Let’s focus on `ngIf`. It's a great example of a structural
|
Let’s focus on `ngIf`. It's a great example of a structural
|
||||||
directive: it takes a boolean and makes an entire chunk of DOM appear
|
directive: it takes a boolean and makes an entire chunk of DOM appear
|
||||||
or disappear.
|
or disappear.
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
The `ngIf` directive does not hide the element.
|
The `ngIf` directive does not hide the element.
|
||||||
Using browser developer tools we can see that, when the condition is true, the top
|
Using browser developer tools we can see that, when the condition is true, the top
|
||||||
paragraph is in the DOM and the bottom disused paragraph is completely
|
paragraph is in the DOM and the bottom disused paragraph is completely
|
||||||
absent from the DOM! In its place are empty `<script>` tags.
|
absent from the DOM! In its place are empty `<script>` tags.
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/structural-directives/element-not-in-dom.png' alt="element not in dom")
|
img(src='/resources/images/devguide/structural-directives/element-not-in-dom.png' alt="element not in dom")
|
||||||
|
|
||||||
@ -66,58 +66,58 @@ figure.image-display
|
|||||||
### Why *remove* rather than *hide*?
|
### Why *remove* rather than *hide*?
|
||||||
We could hide the unwanted paragraph by setting its css `display` style to `none`.
|
We could hide the unwanted paragraph by setting its css `display` style to `none`.
|
||||||
The element would remain in the DOM while invisible. Instead we removed it with `ngIf`.
|
The element would remain in the DOM while invisible. Instead we removed it with `ngIf`.
|
||||||
|
|
||||||
The difference matters. When we hide an element,
|
The difference matters. When we hide an element,
|
||||||
the component's behavior continues.
|
the component's behavior continues.
|
||||||
It remains attached to its DOM element. It continues to listen to events.
|
It remains attached to its DOM element. It continues to listen to events.
|
||||||
Angular keeps checking for changes that could affect data bindings.
|
Angular keeps checking for changes that could affect data bindings.
|
||||||
Whatever the component was doing it keeps doing.
|
Whatever the component was doing it keeps doing.
|
||||||
|
|
||||||
Although invisible, the component — and all of its descendent components —
|
Although invisible, the component — and all of its descendent components —
|
||||||
tie up resources that might be more useful elsewhere.
|
tie up resources that might be more useful elsewhere.
|
||||||
The performance and memory burden can be substantial and the user may not benefit at all.
|
The performance and memory burden can be substantial and the user may not benefit at all.
|
||||||
|
|
||||||
On the positive side, showing the element again is very quick.
|
On the positive side, showing the element again is very quick.
|
||||||
The component's previous state is preserved and ready to display.
|
The component's previous state is preserved and ready to display.
|
||||||
The component doesn't re-initialize — an operation that could be expensive.
|
The component doesn't re-initialize — an operation that could be expensive.
|
||||||
|
|
||||||
`ngIf` is different.
|
`ngIf` is different.
|
||||||
Setting `ngIf` to false **does** affect the component's resource consumption.
|
Setting `ngIf` to false **does** affect the component's resource consumption.
|
||||||
Angular removes the element from DOM, stops change detection for the associated component,
|
Angular removes the element from DOM, stops change detection for the associated component,
|
||||||
detaches it from DOM events (the attachments that it made) and destroys the component.
|
detaches it from DOM events (the attachments that it made) and destroys the component.
|
||||||
The component can be garbage-collected (we hope) and free up memory.
|
The component can be garbage-collected (we hope) and free up memory.
|
||||||
|
|
||||||
Components often have child components which themselves have children.
|
Components often have child components which themselves have children.
|
||||||
All of them are destroyed when `ngIf` destroys the common ancestor.
|
All of them are destroyed when `ngIf` destroys the common ancestor.
|
||||||
This cleanup effort is usually a good thing.
|
This cleanup effort is usually a good thing.
|
||||||
|
|
||||||
Of course it isn't *always* a good thing.
|
Of course it isn't *always* a good thing.
|
||||||
It might be a bad thing if we need that particular component again soon.
|
It might be a bad thing if we need that particular component again soon.
|
||||||
|
|
||||||
The component's state might be expensive to re-construct.
|
The component's state might be expensive to re-construct.
|
||||||
When `ngIf` becomes `true` again, Angular recreates the component and its subtree.
|
When `ngIf` becomes `true` again, Angular recreates the component and its subtree.
|
||||||
Angular runs every component's initialization logic again. That could be expensive ... as when
|
Angular runs every component's initialization logic again. That could be expensive ... as when
|
||||||
a component re-fetches data that had been in memory just moments ago.
|
a component re-fetches data that had been in memory just moments ago.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
*Design thought*: minimize initialization effort and consider caching state in a
|
*Design thought*: minimize initialization effort and consider caching state in a
|
||||||
companion service.
|
companion service.
|
||||||
:marked
|
:marked
|
||||||
Although there are pros and cons to each approach,
|
Although there are pros and cons to each approach,
|
||||||
in general it is best to use `ngIf` to remove unwanted components rather than
|
in general it is best to use `ngIf` to remove unwanted components rather than
|
||||||
hide them.
|
hide them.
|
||||||
|
|
||||||
**These same considerations apply to every structural directive, whether built-in or custom.**
|
**These same considerations apply to every structural directive, whether built-in or custom.**
|
||||||
We should ask ourselves — and the users of our directives — to think carefully
|
We should ask ourselves — and the users of our directives — to think carefully
|
||||||
about the consequences of adding and removing elements and of creating and destroying components.
|
about the consequences of adding and removing elements and of creating and destroying components.
|
||||||
|
|
||||||
Let's see these dynamics at work. For fun, we'll stack the deck *against*
|
Let's see these dynamics at work. For fun, we'll stack the deck *against*
|
||||||
our recommendation and consider a component called `heavy-loader` that
|
our recommendation and consider a component called `heavy-loader` that
|
||||||
***pretends*** to load a ton of data when initialized.
|
***pretends*** to load a ton of data when initialized.
|
||||||
|
|
||||||
We'll display two instances of the component. We toggle the visibility of the first one with CSS.
|
We'll display two instances of the component. We toggle the visibility of the first one with CSS.
|
||||||
We toggle the second into and out of the DOM with `ngIf`.
|
We toggle the second into and out of the DOM with `ngIf`.
|
||||||
|
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`structural-directives/ts/app/structural-directives.component.html,
|
`structural-directives/ts/app/structural-directives.component.html,
|
||||||
structural-directives/ts/app/heavy-loader.component.ts`,
|
structural-directives/ts/app/heavy-loader.component.ts`,
|
||||||
@ -125,22 +125,22 @@ figure.image-display
|
|||||||
'template excerpt, heavy-loader.component.ts')
|
'template excerpt, heavy-loader.component.ts')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We also log when a component is created or destroyed
|
We also log when a component is created or destroyed
|
||||||
using the built-in `ngOnInit` and `ngOnDestroy` lifecycle hooks.
|
using the built-in `ngOnInit` and `ngOnDestroy` [lifecycle hooks](lifecycle-hooks.html).
|
||||||
Here it is in action:
|
Here it is in action:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/structural-directives/heavy-loader-toggle.gif' alt="heavy loader toggle")
|
img(src='/resources/images/devguide/structural-directives/heavy-loader-toggle.gif' alt="heavy loader toggle")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Both components are in the DOM at the start.
|
Both components are in the DOM at the start.
|
||||||
First we toggle the component's visibility repeatedly. The component never leaves the DOM.
|
First we toggle the component's visibility repeatedly. The component never leaves the DOM.
|
||||||
When visible it's always the same instance and the log is quiet.
|
When visible it's always the same instance and the log is quiet.
|
||||||
|
|
||||||
Then we toggle the second component with `ngIf`.
|
Then we toggle the second component with `ngIf`.
|
||||||
We create a new instance every time and the log shows that we're paying
|
We create a new instance every time and the log shows that we're paying
|
||||||
a heavy price to create and destroy it.
|
a heavy price to create and destroy it.
|
||||||
|
|
||||||
If we really expected to "wink" the component like this, toggling visibility would be the better choice.
|
If we really expected to "wink" the component like this, toggling visibility would be the better choice.
|
||||||
In most UIs, when we "close" a component we're unlikely see it again for a long time, if ever.
|
In most UIs, when we "close" a component we're unlikely see it again for a long time, if ever.
|
||||||
The `ngIf` would be preferred in that case.
|
The `ngIf` would be preferred in that case.
|
||||||
@ -149,33 +149,33 @@ figure.image-display
|
|||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## The *<template>* tag
|
## The *<template>* tag
|
||||||
|
|
||||||
Structural directives, like `ngIf`, do their magic by using the
|
Structural directives, like `ngIf`, do their magic by using the
|
||||||
[HTML 5 template tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).
|
[HTML 5 template tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).
|
||||||
|
|
||||||
Outside of an Angular app, the `<template>` tag's default CSS `display` property is `none`.
|
Outside of an Angular app, the `<template>` tag's default CSS `display` property is `none`.
|
||||||
It's contents are ***invisible*** within
|
It's contents are ***invisible*** within
|
||||||
a hidden [document fragment](https://developer.mozilla.org/en/docs/Web/API/DocumentFragment).
|
a hidden [document fragment](https://developer.mozilla.org/en/docs/Web/API/DocumentFragment).
|
||||||
|
|
||||||
Inside of an app, Angular ***removes*** the`<template>` tags and their children.
|
Inside of an app, Angular ***removes*** the`<template>` tags and their children.
|
||||||
The contents are gone — but not forgotten as we'll see soon.
|
The contents are gone — but not forgotten as we'll see soon.
|
||||||
|
|
||||||
We can confirm these effects by wrapping the middle "hip" of the phrase "Hip! Hip! Hooray!" within a `<template>` tag.
|
We can confirm these effects by wrapping the middle "hip" of the phrase "Hip! Hip! Hooray!" within a `<template>` tag.
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'template-tag')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'template-tag')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The display is a 'Hip!' short of perfect enthusiasm. The DOM effects are different when Angular is control.
|
The display is a 'Hip!' short of perfect enthusiasm. The DOM effects are different when Angular is control.
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/structural-directives/template-in-out-of-a2.png' alt="template outside angular")
|
img(src='/resources/images/devguide/structural-directives/template-in-out-of-a2.png' alt="template outside angular")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Evidently Angular replaces the `<template>` tag and its contents with empty `<script>` tags.
|
Evidently Angular replaces the `<template>` tag and its contents with empty `<script>` tags.
|
||||||
That's just its default behavior.
|
That's just its default behavior.
|
||||||
It can do something different as we saw when applying a variety of `ngSwitch` directives to `<template>` tags:
|
It can do something different as we saw when applying a variety of `ngSwitch` directives to `<template>` tags:
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngSwitch')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngSwitch')(format=".")
|
||||||
:marked
|
:marked
|
||||||
When one of those `ngSwitch` conditions is true, Angular inserts the template's content into the DOM.
|
When one of those `ngSwitch` conditions is true, Angular inserts the template's content into the DOM.
|
||||||
|
|
||||||
What does this have to do with `ngIf` and `ngFor`? We didn't use a `<template>` tag with those directives.
|
What does this have to do with `ngIf` and `ngFor`? We didn't use a `<template>` tag with those directives.
|
||||||
|
|
||||||
<a id="asterisk"></a>
|
<a id="asterisk"></a>
|
||||||
@ -186,20 +186,20 @@ figure.image-display
|
|||||||
|
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'asterisk')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'asterisk')(format=".")
|
||||||
:marked
|
:marked
|
||||||
We're prefixing these directive names with an asterisk (\*).
|
We're prefixing these directive names with an asterisk (\*).
|
||||||
|
|
||||||
The asterisk is "syntactic sugar". It simplifies `ngIf` and `ngFor` for both the writer and the reader.
|
The asterisk is "syntactic sugar". It simplifies `ngIf` and `ngFor` for both the writer and the reader.
|
||||||
Under the hood, Angular replaces the asterisk version with a more verbose `<template>` form.
|
Under the hood, Angular replaces the asterisk version with a more verbose `<template>` form.
|
||||||
|
|
||||||
The next two `ngIf` examples are effectively the same and we may write in either style:
|
The next two `ngIf` examples are effectively the same and we may write in either style:
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf-template')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf-template')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Most of us would rather write in style (A).
|
Most of us would rather write in style (A).
|
||||||
|
|
||||||
It's worth knowing that Angular expands style (A) into style (B).
|
It's worth knowing that Angular expands style (A) into style (B).
|
||||||
It moves the paragraph and its contents inside a `<template>` tag.
|
It moves the paragraph and its contents inside a `<template>` tag.
|
||||||
It moves the directive up to the `<template>` tag where it becomes a property binding,
|
It moves the directive up to the `<template>` tag where it becomes a property binding,
|
||||||
surrounded in square brackets. The boolean value of the host component's `condition` property
|
surrounded in square brackets. The boolean value of the host component's `condition` property
|
||||||
determines whether the templated content is displayed or not.
|
determines whether the templated content is displayed or not.
|
||||||
@ -208,37 +208,37 @@ figure.image-display
|
|||||||
|
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngFor-template')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngFor-template')(format=".")
|
||||||
:marked
|
:marked
|
||||||
The basic pattern is the same: create a `<template>`, relocate the content,
|
The basic pattern is the same: create a `<template>`, relocate the content,
|
||||||
and move the directive onto the `<template>`.
|
and move the directive onto the `<template>`.
|
||||||
|
|
||||||
There are extra nuances stemming from
|
There are extra nuances stemming from
|
||||||
Angular's [ngFor micro-syntax](template-syntax#micro-syntax) which expands
|
Angular's [ngFor micro-syntax](template-syntax#micro-syntax) which expands
|
||||||
into an additional `ngForOf` property binding (the iterable) and
|
into an additional `ngForOf` property binding (the iterable) and
|
||||||
the `#hero` [local template variable](template-syntax#local-vars)
|
the `#hero` [local template variable](template-syntax#local-vars)
|
||||||
(the current item in each iteration).
|
(the current item in each iteration).
|
||||||
|
|
||||||
<a id="unless"></a>
|
<a id="unless"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Make a structural directive
|
## Make a structural directive
|
||||||
Let's write our own structural directive, an `Unless` directive, the not-so-evil twin of `ngIf`.
|
Let's write our own structural directive, an `Unless` directive, the not-so-evil twin of `ngIf`.
|
||||||
|
|
||||||
Unlike `ngIf` which displays the template content when `true`,
|
Unlike `ngIf` which displays the template content when `true`,
|
||||||
our directive displays the content when the condition is ***false***.
|
our directive displays the content when the condition is ***false***.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Creating a directive is similar to creating a component.
|
Creating a directive is similar to creating a component.
|
||||||
* import the `Directive` decorator.
|
* import the `Directive` decorator.
|
||||||
|
|
||||||
* add a CSS **attribute selector** (in brackets) that identifies our directive.
|
* add a CSS **attribute selector** (in brackets) that identifies our directive.
|
||||||
|
|
||||||
* specify the name of the public `input` property for binding
|
* specify the name of the public `input` property for binding
|
||||||
(typically the name of the directive itself).
|
(typically the name of the directive itself).
|
||||||
|
|
||||||
* apply the decorator to our implementation class.
|
* apply the decorator to our implementation class.
|
||||||
|
|
||||||
Here is how we begin:
|
Here is how we begin:
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-declaration', 'unless.directive.ts (excerpt)')(format=".")
|
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-declaration', 'unless.directive.ts (excerpt)')(format=".")
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
@ -251,17 +251,17 @@ figure.image-display
|
|||||||
|
|
||||||
We recommend picking a selector name with a prefix to ensure
|
We recommend picking a selector name with a prefix to ensure
|
||||||
that it cannot conflict with any standard HTML attribute, now or in the future.
|
that it cannot conflict with any standard HTML attribute, now or in the future.
|
||||||
|
|
||||||
We do **not** prefix our `unless` directive name with **`ng`**.
|
We do **not** prefix our `unless` directive name with **`ng`**.
|
||||||
That prefix belongs to Angular and
|
That prefix belongs to Angular and
|
||||||
we don't want to confuse our directives with their directives.
|
we don't want to confuse our directives with their directives.
|
||||||
|
|
||||||
Our prefix is `my`.
|
Our prefix is `my`.
|
||||||
:marked
|
:marked
|
||||||
We'll need access to the template *and* something that can render its contents.
|
We'll need access to the template *and* something that can render its contents.
|
||||||
We access the template with a `TemplateRef`. The renderer is a `ViewContainerRef`.
|
We access the template with a `TemplateRef`. The renderer is a `ViewContainerRef`.
|
||||||
We inject both into our constructor as private variables.
|
We inject both into our constructor as private variables.
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-constructor')(format=".")
|
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-constructor')(format=".")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
@ -270,67 +270,67 @@ figure.image-display
|
|||||||
|
|
||||||
Let's add the `myUnless` property now as a setter-only
|
Let's add the `myUnless` property now as a setter-only
|
||||||
[definedProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty).
|
[definedProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty).
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-set')(format=".")
|
+makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-set')(format=".")
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The `@Input()` annotation marks this property as an input for the directive.
|
The `@Input()` annotation marks this property as an input for the directive.
|
||||||
:marked
|
:marked
|
||||||
Nothing fancy here: if the condition is false,
|
Nothing fancy here: if the condition is false,
|
||||||
we render the template, otherwise we clear the element content.
|
we render the template, otherwise we clear the element content.
|
||||||
|
|
||||||
The end result should look like below:
|
The end result should look like below:
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/unless.directive.ts', null, 'unless.directive.ts')
|
+makeExample('structural-directives/ts/app/unless.directive.ts', null, 'unless.directive.ts')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Now we add it to the `directives`array of the host component and try it.
|
Now we add it to the `directives`array of the host component and try it.
|
||||||
First we add some test HTML to the template:
|
First we add some test HTML to the template:
|
||||||
|
|
||||||
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'myUnless')(format=".")
|
+makeExample('structural-directives/ts/app/structural-directives.component.html', 'myUnless')(format=".")
|
||||||
:marked
|
:marked
|
||||||
We run it and it behaves as expected, doing the opposite of `ngIf`.
|
We run it and it behaves as expected, doing the opposite of `ngIf`.
|
||||||
When `condition` is `true`, the top paragraph is removed (replaced by `<script>` tags) and the bottom paragraph appears.
|
When `condition` is `true`, the top paragraph is removed (replaced by `<script>` tags) and the bottom paragraph appears.
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src='/resources/images/devguide/structural-directives/myUnless-is-true.png' alt="myUnless is true" )
|
img(src='/resources/images/devguide/structural-directives/myUnless-is-true.png' alt="myUnless is true" )
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Our `myUnless` directive is dead simple. Surely we left something out.
|
Our `myUnless` directive is dead simple. Surely we left something out.
|
||||||
Surely `ngIf` is more complex?
|
Surely `ngIf` is more complex?
|
||||||
|
|
||||||
[Look at the source code](https://github.com/angular/angular/blob/master/modules/angular2/src/common/directives/ng_if.ts).
|
[Look at the source code](https://github.com/angular/angular/blob/master/modules/angular2/src/common/directives/ng_if.ts).
|
||||||
It's well documented and we shouldn't be shy
|
It's well documented and we shouldn't be shy
|
||||||
about consulting the source when we want to know how something works.
|
about consulting the source when we want to know how something works.
|
||||||
|
|
||||||
`ngIf` isn't much different! There are a few
|
`ngIf` isn't much different! There are a few
|
||||||
additional checks to improve performance (don't clear or recreate the
|
additional checks to improve performance (don't clear or recreate the
|
||||||
view unless necessary) but otherwise it's much the same.
|
view unless necessary) but otherwise it's much the same.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Wrap up
|
## Wrap up
|
||||||
Here is the pertinent source for this chapter.
|
Here is the pertinent source for this chapter.
|
||||||
|
|
||||||
+makeTabs(`
|
+makeTabs(`
|
||||||
structural-directives/ts/app/unless.directive.ts,
|
structural-directives/ts/app/unless.directive.ts,
|
||||||
structural-directives/ts/app/heavy-loader.component.ts,
|
structural-directives/ts/app/heavy-loader.component.ts,
|
||||||
structural-directives/ts/app/structural-directives.component.ts,
|
structural-directives/ts/app/structural-directives.component.ts,
|
||||||
structural-directives/ts/app/structural-directives.component.html
|
structural-directives/ts/app/structural-directives.component.html
|
||||||
`,
|
`,
|
||||||
null,
|
null,
|
||||||
`unless.directive.ts,
|
`unless.directive.ts,
|
||||||
heavy-loader.component.ts,
|
heavy-loader.component.ts,
|
||||||
structural-directives.component.ts,
|
structural-directives.component.ts,
|
||||||
structural-directives.component.html
|
structural-directives.component.html
|
||||||
`)
|
`)
|
||||||
:marked
|
:marked
|
||||||
We learned that we can manipulate our HTML layout with
|
We learned that we can manipulate our HTML layout with
|
||||||
structural directives like `ngFor` and `ngIf` and we
|
structural directives like `ngFor` and `ngIf` and we
|
||||||
wrote our own structural directive, `myUnless`, to do something similar.
|
wrote our own structural directive, `myUnless`, to do something similar.
|
||||||
|
|
||||||
Angular offers more sophisticated techniques for managing layout
|
Angular offers more sophisticated techniques for managing layout
|
||||||
such as *structural components* that can take external content
|
such as *structural components* that can take external content
|
||||||
and incorporate that content within their own templates.
|
and incorporate that content within their own templates.
|
||||||
Tab and tab pane controls are good examples.
|
Tab and tab pane controls are good examples.
|
||||||
|
|
||||||
We'll learn about structural components in a future chapter.
|
We'll learn about structural components in a future chapter.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user