docs: remove outdated docs (#11875)
This commit is contained in:
parent
52812c08e2
commit
33340dbbd1
@ -41,7 +41,6 @@ export abstract class ChangeDetectorRef {
|
||||
* template: `
|
||||
* <cmp><cmp>
|
||||
* `,
|
||||
* directives: [Cmp]
|
||||
* })
|
||||
* class App {
|
||||
* }
|
||||
@ -81,7 +80,6 @@ export abstract class ChangeDetectorRef {
|
||||
* template: `
|
||||
* <li *ngFor="let d of dataProvider.data">Data {{d}}</lig>
|
||||
* `,
|
||||
* directives: [NgFor]
|
||||
* })
|
||||
* class GiantList {
|
||||
* constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) {
|
||||
@ -98,7 +96,6 @@ export abstract class ChangeDetectorRef {
|
||||
* template: `
|
||||
* <giant-list><giant-list>
|
||||
* `,
|
||||
* directives: [GiantList]
|
||||
* })
|
||||
* class App {
|
||||
* }
|
||||
@ -166,7 +163,7 @@ export abstract class ChangeDetectorRef {
|
||||
* @Component({
|
||||
* selector: 'live-data',
|
||||
* inputs: ['live'],
|
||||
* template: `Data: {{dataProvider.data}}`
|
||||
* template: 'Data: {{dataProvider.data}}'
|
||||
* })
|
||||
* class LiveData {
|
||||
* constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) {}
|
||||
@ -186,7 +183,6 @@ export abstract class ChangeDetectorRef {
|
||||
* Live Update: <input type="checkbox" [(ngModel)]="live">
|
||||
* <live-data [live]="live"><live-data>
|
||||
* `,
|
||||
* directives: [LiveData, FORM_DIRECTIVES]
|
||||
* })
|
||||
* class App {
|
||||
* live = true;
|
||||
|
@ -25,13 +25,10 @@ import {DebugContext} from './debug_context';
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'parent',
|
||||
* template: `
|
||||
* <child [prop]="parentProp"></child>
|
||||
* `,
|
||||
* directives: [forwardRef(() => Child)]
|
||||
* template: '<child [prop]="parentProp"></child>',
|
||||
* })
|
||||
* class Parent {
|
||||
* parentProp = "init";
|
||||
* parentProp = 'init';
|
||||
* }
|
||||
*
|
||||
* @Directive({selector: 'child', inputs: ['prop']})
|
||||
@ -41,7 +38,7 @@ import {DebugContext} from './debug_context';
|
||||
* set prop(v) {
|
||||
* // this updates the parent property, which is disallowed during change detection
|
||||
* // this will result in ExpressionChangedAfterItHasBeenCheckedError
|
||||
* this.parent.parentProp = "updated";
|
||||
* this.parent.parentProp = 'updated';
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
|
@ -928,7 +928,6 @@ export interface HostBindingDecorator {
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<input [(ngModel)]="prop">`,
|
||||
* directives: [FORM_DIRECTIVES, NgModelStatus]
|
||||
* })
|
||||
* class App {
|
||||
* prop;
|
||||
@ -968,8 +967,7 @@ export interface HostListenerDecorator {
|
||||
*
|
||||
* Angular will invoke the decorated method when the host element emits the specified event.
|
||||
*
|
||||
* If the decorated method returns `false`, then `preventDefault` is applied on the DOM
|
||||
* event.
|
||||
* If the decorated method returns `false`, then `preventDefault` is applied on the DOM event.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
@ -983,14 +981,13 @@ export interface HostListenerDecorator {
|
||||
*
|
||||
* @HostListener('click', ['$event.target'])
|
||||
* onClick(btn) {
|
||||
* console.log("button", btn, "number of clicks:", this.numberOfClicks++);
|
||||
* console.log('button', btn, 'number of clicks:', this.numberOfClicks++);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `<button counting>Increment</button>`,
|
||||
* directives: [CountClicks]
|
||||
* template: '<button counting>Increment</button>',
|
||||
* })
|
||||
* class App {}
|
||||
* ```
|
||||
|
@ -40,7 +40,6 @@ import {NgZoneImpl} from './ng_zone_impl';
|
||||
* <button (click)="processWithinAngularZone()">Process within Angular zone</button>
|
||||
* <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
|
||||
* `,
|
||||
* directives: [NgIf]
|
||||
* })
|
||||
* export class NgZoneDemo {
|
||||
* progress: number = 0;
|
||||
|
@ -1,10 +0,0 @@
|
||||
@name Change Detection
|
||||
@description
|
||||
# Change Detection
|
||||
|
||||
* Mechanisms by which changes are detected in the model
|
||||
* DAG
|
||||
* Order of evaluation
|
||||
* Pure Functions
|
||||
* `onChange` method
|
||||
* Parser
|
@ -1,7 +0,0 @@
|
||||
# Expressions
|
||||
|
||||
## Binding Semantics
|
||||
|
||||
### Formatters
|
||||
|
||||
## Statement Semantics
|
@ -1,3 +0,0 @@
|
||||
# Record
|
||||
|
||||
## RecordRange
|
@ -1,3 +0,0 @@
|
||||
# Overview
|
||||
|
||||
* High level description of all of the components.
|
@ -1,610 +0,0 @@
|
||||
# Templates
|
||||
|
||||
Templates are markup which is added to HTML to declaratively describe how the application model should be
|
||||
projected to DOM as well as which DOM events should invoke which methods on the controller. Templates contain
|
||||
syntaxes which are core to Angular and allows for data-binding, event-binding, template-instantiation.
|
||||
|
||||
The design of the template syntax has these properties:
|
||||
|
||||
|
||||
* All data-binding expressions are easily identifiable. (i.e. there is never an ambiguity whether the value should be
|
||||
interpreted as string literal or as an expression.)
|
||||
* All events and their statements are easily identifiable.
|
||||
* All places of DOM instantiation are easily identifiable.
|
||||
* All places of variable declaration are easily identifiable.
|
||||
|
||||
The above properties guarantee that the templates are easy to parse by tools (such as IDEs) and reason about by people.
|
||||
At no point is it necessary to understand which directives are active or what their semantics are in order to reason
|
||||
about the template runtime characteristics.
|
||||
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
Below is a summary of the kinds of syntaxes which Angular templating supports. The syntaxes are explained in more
|
||||
detail in the following sections.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Description</th><th>Short</th><th>Canonical</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Text Interpolation</th>
|
||||
<td>
|
||||
<pre>
|
||||
<div>{{exp}}</div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
<pre>
|
||||
<div>
|
||||
Hello {{name}}!
|
||||
<br>
|
||||
Goodbye {{name}}!
|
||||
</div>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
<div [text|index]="exp"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
<pre>
|
||||
<div
|
||||
[text|0]=" 'Hello' + stringify(name) + '!' "
|
||||
[text|2]=" 'Goodbye' + stringify(name) + '!' ">
|
||||
<b>x</b>
|
||||
</div>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Property Interpolation</th>
|
||||
<td>
|
||||
<pre>
|
||||
<div name="{{exp}}"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<div class="{{selected}}"></div>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
<div [name]="stringify(exp)"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<div [title]="stringify(selected)"></div>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Property binding</th>
|
||||
<td>
|
||||
<pre>
|
||||
<div [prop]="exp"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<div [hidden]="true"></div>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
<div bind-prop="exp"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<div bind-hidden="true"></div>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Event binding (non-bubbling)</th>
|
||||
<td>
|
||||
<pre>
|
||||
<div (event)="statement"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<div (click)="doX()"></div>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
<div on-event="statement"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<video #player>
|
||||
<button (click)="player.play()">play</button>
|
||||
</video>
|
||||
</pre>
|
||||
|
||||
Or:
|
||||
|
||||
<pre>
|
||||
<div def="symbol"></div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<video def="player">
|
||||
<button on-click="player.play()">play</button>
|
||||
</video>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Inline Template</th>
|
||||
<td>
|
||||
<pre>
|
||||
<div template="...">...</div>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<ul>
|
||||
<li template="for: #item of items">
|
||||
{{item}}
|
||||
</li>
|
||||
</ul>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
<template>...</template>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
<pre>
|
||||
<ul>
|
||||
<template def-for:"item"
|
||||
bind-for-in="items">
|
||||
<li>
|
||||
{{item}}
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Explicit Template</th>
|
||||
<td>
|
||||
<pre>
|
||||
<template>...</template>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<template #for="item"
|
||||
[for-in]="items">
|
||||
_some_content_to_repeat_
|
||||
</template>
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
<template>...</template>
|
||||
</pre>
|
||||
|
||||
Example:
|
||||
|
||||
<pre>
|
||||
<template def-for="item"
|
||||
bind-for-in="items">
|
||||
_some_content_to_repeat_
|
||||
</template>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
## Property Binding
|
||||
|
||||
Binding application model data to the UI is the most common kind of bindings in an Angular application. The bindings
|
||||
are always in the form of `property-name` which is assigned an `expression`. The generic form is:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Short form</th>
|
||||
<td><pre><some-element [someProperty]="expression"></pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Canonical form</th>
|
||||
<td><pre><some-element bind-some-property="expression"></pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Where:
|
||||
* `some-element` can be any existing DOM element.
|
||||
* `someProperty` or `some-property` (escaped with `[]` or `bind-`) is the name of the property on `some-element`. If
|
||||
the property is dash-case, it will be converted into camel-case `someProperty`.
|
||||
* `expression` is a valid expression (as defined in section below).
|
||||
|
||||
Example:
|
||||
```
|
||||
<div [title]="user.firstName">
|
||||
```
|
||||
|
||||
In the above example the `title` property of the `div` element will be updated whenever the `user.firstName` changes
|
||||
its value.
|
||||
|
||||
Key points:
|
||||
* The binding is to the element property not the element attribute.
|
||||
* To prevent custom element from accidentally reading the literal `expression` on the title element, the attribute name
|
||||
is escaped. In our case the `title` is escaped to `[title]` through the addition of square brackets `[]`.
|
||||
* A binding value (in this case `user.firstName`) will always be an expression, never a string literal.
|
||||
|
||||
NOTE: Unlike Angular v1, Angular v2 binds to properties of elements rather than attributes of elements. This is
|
||||
done to better support custom elements, and to allow binding for values other than strings.
|
||||
|
||||
NOTE: Some editors/server side pre-processors may have trouble generating `[]` around the attribute name. For this
|
||||
reason Angular also supports a canonical version which is prefixed using `bind-`.
|
||||
|
||||
|
||||
|
||||
### String Interpolation
|
||||
|
||||
Property bindings are the only data bindings which Angular supports, but for convenience Angular supports an interpolation
|
||||
syntax which is just a short hand for the data binding syntax.
|
||||
|
||||
```
|
||||
<span>Hello {{name}}!</span>
|
||||
```
|
||||
|
||||
is a short hand for:
|
||||
|
||||
```
|
||||
<span [text|0]=" 'Hello ' + stringify(name) + '!' "></span>
|
||||
```
|
||||
|
||||
The above says to bind the `'Hello ' + stringify(name) + '!'` expression to the zero-th child of the `span`'s `text`
|
||||
property. The index is necessary in case there are more than one text nodes, or if the text node we wish to bind to
|
||||
is not the first one.
|
||||
|
||||
Similarly the same rules apply to interpolation inside attributes.
|
||||
|
||||
```
|
||||
<span title="Hello {{name}}!"></span>
|
||||
```
|
||||
|
||||
is a short hand for:
|
||||
|
||||
```
|
||||
<span [title]=" 'Hello ' + stringify(name) + '!' "></span>
|
||||
```
|
||||
|
||||
NOTE: `stringify()` is a built in implicit function which converts its argument to a string representation, while
|
||||
keeping `null` and `undefined` as empty strings.
|
||||
|
||||
|
||||
|
||||
|
||||
## Local Variables
|
||||
|
||||
|
||||
|
||||
|
||||
## Inline Templates
|
||||
|
||||
Data binding allows updating the DOM's properties, but it does not allow for changing of the DOM structure. To change
|
||||
DOM structure we need the ability to define child templates, and then instantiate these templates into Views. The
|
||||
Views than can be inserted and removed as needed to change the DOM structure.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Short form</th>
|
||||
<td>
|
||||
<pre>
|
||||
parent template
|
||||
<element>
|
||||
<some-element template="instantiating-directive-microsyntax">child template</some-element>
|
||||
</element>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Canonical form</th>
|
||||
<td>
|
||||
<pre>
|
||||
parent template
|
||||
<element>
|
||||
<template instantiating-directive-bindings>
|
||||
<some-element>child template</some-element>
|
||||
</template>
|
||||
</element>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Where:
|
||||
* `template` defines a child template and designates the anchor where Views (instances of the template) will be
|
||||
inserted. The template can be defined implicitly with `template` attribute, which turns the current element into
|
||||
a template, or explicitly with `<template>` element. Explicit declaration is longer, but it allows for having
|
||||
templates which have more than one root DOM node.
|
||||
* `viewport` is required for templates. The directive is responsible for deciding when
|
||||
and in which order should child views be inserted into this location. Such a directive usually has one or
|
||||
more bindings and can be represented as either `viewport-directive-bindings` or
|
||||
`viewport-directive-microsyntax` on `template` element or attribute. See template microsyntax for more details.
|
||||
|
||||
|
||||
Example of conditionally included template:
|
||||
|
||||
```
|
||||
Hello {{user}}!
|
||||
<div template="ngIf: isAdministrator">
|
||||
...administrator menu here...
|
||||
</div>
|
||||
```
|
||||
|
||||
In the above example the `ngIf` directive determines whether the child view (an instance of the child template) should be
|
||||
inserted into the root view. The `ngIf` makes this decision based on if the `isAdministrator` binding is true.
|
||||
|
||||
The above example is in the short form, for better clarity let's rewrite it in the canonical form, which is functionally
|
||||
identical.
|
||||
|
||||
```
|
||||
Hello {{user}}!
|
||||
<template [ngIf]="isAdministrator">
|
||||
<div>
|
||||
...administrator menu here...
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
|
||||
### Template Microsyntax
|
||||
|
||||
Often times it is necessary to encode a lot of different bindings into a template to control how the instantiation
|
||||
of the templates occurs. One such example is `ngFor`.
|
||||
|
||||
```
|
||||
<form #foo=form>
|
||||
</form>
|
||||
<ul>
|
||||
<template [ngFor] #person [ngForOf]="people" #i="index">
|
||||
<li>{{i}}. {{person}}<li>
|
||||
</template>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Where:
|
||||
* `[ngFor]` triggers the for directive.
|
||||
* `#person` exports the implicit `ngFor` item.
|
||||
* `[ngForOf]="people"` binds an iterable object to the `ngFor` controller.
|
||||
* `#i=index` exports item index as `i`.
|
||||
|
||||
The above example is explicit but quite wordy. For this reason in most situations a short hand version of the
|
||||
syntax is preferable.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor; #person; of=people; #i=index;">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Notice how each key value pair is translated to a `key=value;` statement in the `template` attribute. This makes the
|
||||
repeat syntax a much shorter, but we can do better. Turns out that most punctuation is optional in the short version
|
||||
which allows us to further shorten the text.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor #person of people #i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
We can also optionally use `var` instead of `#` and add `:` to `for` which creates the following recommended
|
||||
microsyntax for `ngFor`.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor: var person of people; var i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Finally, we can move the `ngFor` keyword to the left hand side and prefix it with `*` as so:
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li *ngFor="let person of people; var i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
|
||||
The format is intentionally defined freely, so that developers of directives can build an expressive microsyntax for
|
||||
their directives. The following code describes a more formal definition.
|
||||
|
||||
```
|
||||
expression: ... // as defined in Expressions section
|
||||
local: [a-zA-Z][a-zA-Z0-9]* // exported variable name available for binding
|
||||
internal: [a-zA-Z][a-zA-Z0-9]* // internal variable name which the directive exports.
|
||||
key: [a-z][-|_|a-z0-9]]* // key which maps to attribute name
|
||||
keyExpression: key[:|=]?expression // binding which maps an expression to a property
|
||||
varExport: [#|var]local(=internal)? // binding which exports a local variable from a directive
|
||||
microsyntax: ([[key|keyExpression|varExport][;|,]?)*
|
||||
```
|
||||
|
||||
Where
|
||||
* `expression` is an Angular expression as defined in section: Expressions.
|
||||
* `local` is a local identifier for local variables.
|
||||
* `internal` is an internal variable which the directive exports for binding.
|
||||
* `key` is an attribute name usually only used to trigger a specific directive.
|
||||
* `keyExpression` is a property name to which the expression will be bound to.
|
||||
* `varExport` allows exporting of directive internal state as variables for further binding. If no `internal` name
|
||||
is specified, the exporting is to an implicit variable.
|
||||
* `microsyntax` allows you to build a simple microsyntax which can still clearly identify which expressions bind to
|
||||
which properties as well as which variables are exported for binding.
|
||||
|
||||
|
||||
NOTE: the `template` attribute must be present to make it clear to the user that a sub-template is being created. This
|
||||
goes along with the philosophy that the developer should be able to reason about the template without understanding the
|
||||
semantics of the instantiator directive.
|
||||
|
||||
|
||||
|
||||
|
||||
## Binding Events
|
||||
|
||||
Binding events allows wiring events from DOM (or other components) to the Angular controller.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Short form</th>
|
||||
<td><pre><some-element (someEvent)="statement"></pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Canonical form</th>
|
||||
<td><pre><some-element on-some-event="statement"></pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Where:
|
||||
* `some-element` Any element which can generate DOM events (or has an angular directive which generates the event).
|
||||
* `someEvent` or `some-event` (escaped with `()` or `on-`) is the name of the event `some-event`. If the event is
|
||||
dash-case, it will be converted into camel-case `someEvent`.
|
||||
* `statement` is a valid statement (as defined in section below).
|
||||
If the execution of the statement returns `false`, then `preventDefault`is applied on the DOM event.
|
||||
|
||||
Angular listens to bubbled DOM events (as in the case of clicking on any child), as shown below:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Short form</th>
|
||||
<td><pre><some-element (some-event)="statement"></pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Canonical form</th>
|
||||
<td><pre><some-element on-some-event="statement"></pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Example:
|
||||
```
|
||||
@Component(...)
|
||||
class Example {
|
||||
submit() {
|
||||
// do something when button is clicked
|
||||
}
|
||||
}
|
||||
|
||||
<button (click)="submit()">Submit</button>
|
||||
```
|
||||
|
||||
In the above example, when clicking on the submit button angular will invoke the `submit` method on the surrounding
|
||||
component's controller.
|
||||
|
||||
|
||||
NOTE: Unlike Angular v1, Angular v2 treats event bindings as core constructs not as directives. This means that there
|
||||
is no need to create an event directive for each kind of event. This makes it possible for Angular v2 to easily
|
||||
bind to custom events of Custom Elements, whose event names are not known ahead of time.
|
||||
|
||||
|
||||
|
||||
|
||||
## Expressions, Statements and Formatters
|
||||
|
||||
Angular templates contain expressions for binding to data and statements for binding to events. Expressions and statements
|
||||
have different semantics.
|
||||
|
||||
|
||||
### Expressions
|
||||
|
||||
Expressions can be used to bind to properties only. Expressions represent how data should be projected to the View.
|
||||
Expressions should not have any side effect and should be idempotent. Examples of where expressions can be used in
|
||||
Angular are:
|
||||
```
|
||||
<div title="{{expression}}">{{expression}}</div>
|
||||
<div [title]="expression">...</div>
|
||||
<div bind-title="expression">...</div>
|
||||
<div template="ngIf: expression">...</div>
|
||||
```
|
||||
|
||||
Expressions are simplified version of expression in the language in which you are writing your application. (i.e.
|
||||
expressions follow JS syntax and semantics in JS and Dart syntax and semantics in Dart). Unlike expressions in the
|
||||
language, binding expressions behave differently in following ways:
|
||||
|
||||
* *Must be defined*: Unlike Angular v1, Angular v2 will throw an error on dereferencing fields which are not defined.
|
||||
For example: `user.name` will throw an error if `user` is defined but it does not have `name` property. If the `name`
|
||||
property is not known, it must be declared and set to some value such as empty string, `null` (or `undefined` in JS).
|
||||
This is done to allow early detection of errors in the templates.
|
||||
* *Safe dereference*: Expressions `user.name` where `user` is null will throw `NullPointerException` in the language.
|
||||
In contrast Angular will silently ignore `null` on `user`. This is done because Views often have to wait for the data
|
||||
to arrive from the backend and many fields will be `null` until the data arrives. Safe dereference is so common in the
|
||||
Views that we have made it the default.
|
||||
* *Single expression*: An expression must be a single statement. (i.e. no `;`)
|
||||
* *No assignments*: Binding expressions can not contain assignments.
|
||||
* *No keywords*: Binding expressions can not contain keywords such as: `var`, `if`, and so on.
|
||||
* *Formatters*: Angular expressions can be piped through formatters to further transform the binding value.
|
||||
(See: Formatters)
|
||||
|
||||
Examples of some expressions and their behavior:
|
||||
|
||||
Given:
|
||||
```
|
||||
class Greeter {
|
||||
name:string;
|
||||
}
|
||||
```
|
||||
|
||||
* `name` : Will result in the value of the `name` property on the `Greeter` class.
|
||||
* `name.length`: Will result in either the length of the `name` string or `undefined` (`null` in Dart) if `name`
|
||||
property is `null` or `undefined`. Example of: safe dereference.
|
||||
* `foo`: Will throw an error because `foo` is not declared on the `Greeter` class. Example of: Must be defined
|
||||
* `name=1`: Not allowed because of assignment.
|
||||
* `name; name.length`: Not allowed because of multiple statements.
|
||||
|
||||
|
||||
|
||||
### Statements
|
||||
|
||||
Statements can be used to bind to events only. Statements represent actions to trigger as a response to an event.
|
||||
Examples of where statements can be used in Angular are:
|
||||
```
|
||||
<div (click)="statements">...</div>
|
||||
<div on-click="statements">...</div>
|
||||
```
|
||||
|
||||
Statements are similar to statements in the language in which you are writing your application. (i.e.
|
||||
statements follow JS syntax and semantics in JS and Dart syntax and semantics in Dart). Unlike statements in the
|
||||
language, binding expressions behave differently in the following ways:
|
||||
|
||||
* *Unsafe dereference*: Expressions `user.verify()` where `user` is `null` will throw `NullPointerException` in the
|
||||
language as well as in statements. (In contrast to Safe dereference in Angular expressions.) While Angular protects
|
||||
you from null dereferencing in expressions due to lazy loading of data, no such protection is required for statements,
|
||||
and doing so would make it harder to detect typos in statements.
|
||||
* *Multiple statements OK*: Statements can be composed from more than one statement. (i.e. no `doA(); doB()`)
|
||||
* *Assignments OK*: Event bindings can have side effects and hence assignments are allowed.
|
||||
* *No keywords*: Statements can not contain keywords such as: `var`, `if`, and so on.
|
||||
* *No Formatters*: Angular statements can not contain formatters. (Formatters are only useful for data binding)
|
||||
|
||||
## Further Reading
|
||||
|
||||
* [Template Syntax Constraints and Reasoning](https://docs.google.com/document/d/1HHy_zPLGqJj0bHMiWPzPCxn1pO5GlOYwmv-qGgl4f_s)
|
@ -1,378 +0,0 @@
|
||||
# Directives
|
||||
|
||||
Directives are classes which get instantiated as a response to a particular DOM structure. By controlling the DOM structure, what directives are imported, and their selectors, the developer can use the [composition pattern](http://en.wikipedia.org/wiki/Object_composition) to get a desirable application behavior.
|
||||
|
||||
Directives are the cornerstone of an Angular application. We use Directives to break complex problems into smaller more reusable components. Directives allow the developer to turn HTML into a DSL and then control the application assembly process.
|
||||
|
||||
Angular applications do not have a main method. Instead they have a root Component. Dependency Injection then assembles the directives into a working Angular application.
|
||||
|
||||
Directives with an encapsulated view and an optional injector are called *Components*.
|
||||
|
||||
|
||||
## CSS Selectors
|
||||
|
||||
Directives are instantiated whenever the CSS selector matches the DOM structure.
|
||||
|
||||
Angular supports these CSS selector constructs:
|
||||
* Element name: `name`
|
||||
* Attribute: `[attribute]`
|
||||
* Attribute has value: `[attribute=value]`
|
||||
* Attribute contains value: `[attribute*=value]`
|
||||
* Class: `.class`
|
||||
* AND operation: `name[attribute]`
|
||||
* OR operation: `name,.class`
|
||||
* NOT operation: `:not(.class)`
|
||||
|
||||
Angular does not support these (and any CSS selector which crosses element boundaries):
|
||||
* Descendant: `body div`
|
||||
* Direct descendant: `body > div`
|
||||
* Adjacent: `div + table`
|
||||
* Sibling: `div ~ table`
|
||||
* Wildcard: `*`
|
||||
* ID: `#id`
|
||||
* Pseudo selectors: `:pseudo` other than `:not`
|
||||
|
||||
|
||||
|
||||
Given this DOM:
|
||||
|
||||
```<input type="text" required class="primary">```
|
||||
|
||||
These CSS selectors will match:
|
||||
* `input`: Triggers whenever element name is `input`.
|
||||
* `[required]`: Triggers whenever element contains a required attribute.
|
||||
* `[type=text]`: Triggers whenever element contains attribute `type` whose value is `text`.
|
||||
* `.primary`: Triggers whenever element class contains `primary`.
|
||||
|
||||
CSS Selectors can be combined:
|
||||
* `input[type=text]`: Triggers on element name `input` which is of `type` `text`.
|
||||
* `input[type=text], textarea`: triggers on element name `input` which is of `type` `text` or element name `textarea`.
|
||||
|
||||
|
||||
|
||||
## Directives
|
||||
|
||||
The simplest kind of directive is a decorator. Directives are useful for encapsulating behavior.
|
||||
|
||||
* Multiple decorators can be placed on a single element.
|
||||
* Directives do not introduce new evaluation context.
|
||||
* Directives are registered through the `@Directive` meta-data annotation.
|
||||
|
||||
Here is a trivial example of a tooltip decorator. The directive will log a tooltip into the console on every time mouse enters a region:
|
||||
|
||||
```
|
||||
@Directive({
|
||||
selector: '[tooltip]', | CSS Selector which triggers the decorator
|
||||
properties: [ | List which properties need to be bound
|
||||
'text: tooltip' | - DOM element tooltip property should be
|
||||
], | mapped to the directive text property.
|
||||
host: { | List which events need to be mapped.
|
||||
'(mouseover)': 'show()' | - Invoke the show() method every time
|
||||
} | the mouseover event is fired.
|
||||
}) |
|
||||
class Form { | Directive controller class, instantiated
|
||||
| when CSS matches.
|
||||
text:string; | text property on the Directive Controller.
|
||||
|
|
||||
show() { | Show method which implements the show action.
|
||||
console.log(this.text); |
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example of usage:
|
||||
|
||||
```<span tooltip="Tooltip text goes here.">Some text here.</span>```
|
||||
|
||||
The developer of an application can now freely use the `tooltip` attribute wherever the behavior is needed. The code above has taught the browser a new reusable and declarative behavior.
|
||||
|
||||
Notice that data binding will work with this decorator with no further effort as shown below.
|
||||
|
||||
```<span tooltip="Greetings {{user}}!">Some text here.</span>```
|
||||
|
||||
|
||||
|
||||
## Components
|
||||
|
||||
Component is a directive which uses shadow DOM to create encapsulate visual behavior. Components are typically used to create UI widgets or to break up the application into smaller components.
|
||||
|
||||
* Only one component can be present per DOM element.
|
||||
* Component's CSS selectors usually trigger on element names. (Best practice)
|
||||
* Component has its own shadow view which is attached to the element as a Shadow DOM.
|
||||
* Shadow view context is the component instance. (i.e. template expressions are evaluated against the component instance.)
|
||||
|
||||
>> TODO(misko): Configuring the injector
|
||||
|
||||
Example of a component:
|
||||
|
||||
```
|
||||
@Component({ | Component annotation
|
||||
selector: 'pane', | CSS selector on <pane> element
|
||||
properties: [ | List which property need to be bound
|
||||
'title', | - title mapped to component title
|
||||
'open' | - open attribute mapped to component's open property
|
||||
], |
|
||||
templateUrl: 'pane.html' | URL of template HTML
|
||||
}) |
|
||||
class Pane { | Component controller class
|
||||
title:string; | - title property
|
||||
open:boolean;
|
||||
|
||||
constructor() {
|
||||
this.title = '';
|
||||
this.open = true;
|
||||
}
|
||||
|
||||
// Public API
|
||||
toggle() => this.open = !this.open;
|
||||
open() => this.open = true;
|
||||
close() => this.open = false;
|
||||
}
|
||||
```
|
||||
|
||||
`pane.html`:
|
||||
```
|
||||
<div class="outer">
|
||||
<h1>{{title}}</h1>
|
||||
<div class="inner" [hidden]="!open">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
`pane.css`:
|
||||
```
|
||||
.outer, .inner { border: 1px solid blue;}
|
||||
.h1 {background-color: blue;}
|
||||
```
|
||||
|
||||
Example of usage:
|
||||
```
|
||||
<pane #pane title="Example Title" open="true">
|
||||
Some text to wrap.
|
||||
</pane>
|
||||
<button (click)="pane.toggle()">toggle</button>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Directives that use a ViewContainer
|
||||
|
||||
Directives that use a ViewContainer can control instantiation of child views which are then inserted into the DOM. (Examples are `ngIf` and `ngFor`.)
|
||||
|
||||
* Every `template` element creates a `ProtoView` which can be used to create Views via the ViewContainer.
|
||||
* The child views show up as siblings of the directive in the DOM.
|
||||
|
||||
>> TODO(misko): Relationship with Injection
|
||||
>> TODO(misko): Instantiator can not be injected into child Views
|
||||
|
||||
|
||||
```
|
||||
@Directive({
|
||||
selector: '[if]',
|
||||
properties: [
|
||||
'condition: if'
|
||||
]
|
||||
})
|
||||
export class If {
|
||||
viewContainer: ViewContainerRef;
|
||||
protoViewRef: ProtoViewRef;
|
||||
view: View;
|
||||
|
||||
constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
this.viewContainer = viewContainer;
|
||||
this.protoViewRef = protoViewRef;
|
||||
this.view = null;
|
||||
}
|
||||
|
||||
set condition(value) {
|
||||
if (value) {
|
||||
if (this.view === null) {
|
||||
this.view = this.viewContainer.create(protoViewRef);
|
||||
}
|
||||
} else {
|
||||
if (this.view !== null) {
|
||||
this.viewContainer.remove(this.view);
|
||||
this.view = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Dependency Injection (DI) is a key aspect of directives. DI allows directives to be assembled into different [compositional](http://en.wikipedia.org/wiki/Object_composition) hierarchies. Angular encourages [composition over inheritance](http://en.wikipedia.org/wiki/Composition_over_inheritance) in the application design (but inheritance based approaches are still supported).
|
||||
|
||||
When Angular directives are instantiated, the directive can ask for other related directives to be injected into it. By assembling the directives in different order and subtypes the application behavior can be controlled. A good mental model is that the DOM structure controls the directive instantiation graph.
|
||||
|
||||
Directive instantiation is triggered by the directive CSS selector matching the DOM structure. In a directive's constructor, it can ask for other directives or application services. When asking for directives, the dependency is attempted to be located by its DOM hierarchy first, then if not found, by using the application level injector.
|
||||
|
||||
To better understand the kinds of injections which are supported in Angular we have broken them down into use case examples.
|
||||
|
||||
|
||||
### Injecting Services
|
||||
|
||||
Service injection is the most straight forward kind of injection which Angular supports. It involves a component configuring the `providers` or `viewProviders` and then letting the directive ask for the configured service.
|
||||
|
||||
This example illustrates how to inject `MyService` into `House` directive.
|
||||
|
||||
|
||||
```
|
||||
class MyService {} | Assume a service which needs to be injected
|
||||
| into a directive.
|
||||
|
|
||||
@Component({ | Assume a top level application component which
|
||||
selector: 'my-app', | configures the services to be injected.
|
||||
viewProviders: [MyService], |
|
||||
templateUrl: 'my_app.html', | Assume we have a template that needs to be
|
||||
directives: [House] | configured with directives to be injected.
|
||||
}) |
|
||||
class MyApp {} |
|
||||
|
|
||||
@Directive({ | This is the directive into which we would like
|
||||
selector: '[house]' | to inject the MyService.
|
||||
}) |
|
||||
class House { |
|
||||
constructor(myService:MyService) { | Notice that in the constructor we can simply
|
||||
} | ask for MyService.
|
||||
} |
|
||||
|
||||
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `my_app.html`:
|
||||
```
|
||||
<div house> | The house attribute triggers the creation of the House directive.
|
||||
</div> | This is equivalent to:
|
||||
| new House(injector.get(MyService));
|
||||
```
|
||||
|
||||
|
||||
### Injecting other Directives
|
||||
|
||||
Injecting other directives into directives follows a similar mechanism as injecting services into directives, but with added constraint of visibility governed by DOM structure.
|
||||
|
||||
There are five kinds of visibilities:
|
||||
|
||||
* (no annotation): Inject dependent directives only if they are on the current element.
|
||||
* `@SkipSelf()`: Inject a directive if it is at any element above the current element.
|
||||
* `@child`: Inject a list of direct children which match a given type. (Used with `Query`)
|
||||
* `@descendant`: Inject a list of any children which match a given type. (Used with `Query`)
|
||||
|
||||
NOTE: if the injection constraint can not be satisfied by the current visibility constraint, then it is forwarded to the normal injector which either provides a default value for the directive or throws an error.
|
||||
|
||||
Here is an example of the kinds of injections which can be achieved:
|
||||
|
||||
|
||||
```
|
||||
@Component({ |
|
||||
selector: 'my-app' |
|
||||
templateUrl: 'my_app.html', |
|
||||
directives: [Form, FieldSet, |
|
||||
Field, Primary] |
|
||||
}) |
|
||||
class MyApp {} |
|
||||
|
|
||||
@Directive({ selector: 'form' }) |
|
||||
class Form { |
|
||||
constructor( |
|
||||
@descendant sets:Query<FieldSet> |
|
||||
) { |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Directive({ selector: 'fieldset' }) |
|
||||
class FieldSet { |
|
||||
constructor( |
|
||||
@child sets:Query<Field> |
|
||||
) { ... } |
|
||||
} |
|
||||
|
|
||||
@Directive({ selector: 'field' }) |
|
||||
class Field { |
|
||||
constructor( |
|
||||
@SkipSelf() field:Form, |
|
||||
@SkipSelf() field:FieldSet, |
|
||||
) { ... } |
|
||||
} |
|
||||
|
|
||||
@Directive({ selector: '[primary]'}) |
|
||||
class Primary { |
|
||||
constructor(field:Field ) { ... } |
|
||||
} |
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `my_app.html`:
|
||||
```
|
||||
<form> |
|
||||
<div> |
|
||||
<fieldset> |
|
||||
<field primary></field> |
|
||||
<field></field> |
|
||||
</fieldset> |
|
||||
</div> |
|
||||
</form> |
|
||||
```
|
||||
|
||||
#### Shadow DOM effects on Directive DI
|
||||
|
||||
Shadow DOM provides an encapsulation for components, so as a general rule it does not allow directive injections to cross the shadow DOM boundaries. To remedy this, declaratively specify the required component as an injectable.
|
||||
|
||||
```
|
||||
@Component({
|
||||
selector: '[kid]'
|
||||
templateUrl: 'kid.html',
|
||||
directives: []
|
||||
})
|
||||
class Kid {
|
||||
constructor(
|
||||
@SkipSelf() dad:Dad,
|
||||
@Optional() grandpa:Grandpa
|
||||
) {
|
||||
this.name = 'Billy';
|
||||
this.dad = dad.name;
|
||||
this.grandpa = grandpa.name;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[dad]'
|
||||
templateUrl: 'dad.html',
|
||||
directives: [Kid]
|
||||
})
|
||||
class Dad {
|
||||
constructor(@SkipSelf() dad:Grandpa) {
|
||||
this.name = 'Joe Jr';
|
||||
this.dad = dad.name;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[grandpa]',
|
||||
viewProviders: [],
|
||||
templateUrl: 'grandpa.html',
|
||||
directives: [Dad]
|
||||
})
|
||||
class Grandpa {
|
||||
constructor() {
|
||||
this.name = 'Joe';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `grandpa.html`: The Dad has access to the Grandpa.
|
||||
```
|
||||
Name: {{name}}: <br> Children: <div dad></div>
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `dad.html`: Here the rendered Kid will also have access to Grandpa.
|
||||
```
|
||||
Name: {{name}}: <br> Dad: {{dad}} <br> Children: <div kid></div>
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
* [Composition](http://en.wikipedia.org/wiki/Object_composition)
|
||||
* [Composition over Inheritance](http://en.wikipedia.org/wiki/Composition_over_inheritance)
|
@ -1,27 +0,0 @@
|
||||
# Pipes
|
||||
|
||||
Pipes can be appended on the end of the expressions to translate the value to a different format. Typically used
|
||||
to control the stringification of numbers, dates, and other data, but can also be used for ordering, mapping, and
|
||||
reducing arrays. Pipes can be chained.
|
||||
|
||||
NOTE: Pipes are known as filters in Angular v1.
|
||||
|
||||
Pipes syntax is:
|
||||
|
||||
```
|
||||
<div class="movie-copy">
|
||||
<p>
|
||||
{{ model.getValue('copy') | async | uppercase }}
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Starring</b>: {{ model.getValue('starring') | async }}
|
||||
</li>
|
||||
<li>
|
||||
<b>Genres</b>: {{ model.getValue('genres') | async }}
|
||||
</li>
|
||||
<ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
# Components
|
||||
|
||||
* Different kinds of injections and visibility
|
||||
* Shadow DOM usage
|
||||
*
|
@ -1,21 +0,0 @@
|
||||
# HTML Template Compilation
|
||||
|
||||
* Process by which a HTML template is converted to a ProtoView
|
||||
|
||||
1. DOM parse()
|
||||
2. Macro
|
||||
3.
|
||||
|
||||
# HTML Template
|
||||
|
||||
* https://docs.google.com/document/d/1HHy_zPLGqJj0bHMiWPzPCxn1pO5GlOYwmv-qGgl4f_s
|
||||
* https://docs.google.com/document/d/1kpuR512G1b0D8egl9245OHaG0cFh0ST0ekhD_g8sxtI
|
||||
* https://docs.google.com/document/d/1DuFQKElIq293FlhQHvAp__NfP1XV9r8femZFMigm4-k
|
||||
* Must understand without understanding directives
|
||||
* IDE support
|
||||
* local variables
|
||||
|
||||
|
||||
# Binding Philosophy
|
||||
|
||||
# Binding to Shadow DOM
|
@ -1,246 +0,0 @@
|
||||
# View
|
||||
|
||||
## Overview
|
||||
|
||||
This document explains the concept of a View.
|
||||
A View is a core primitive used by angular to render the DOM tree.
|
||||
A ViewContainer is location in a View which can accept child Views.
|
||||
Every ViewContainer has an associated ViewContainerRef than can contain any number of child Views.
|
||||
Views form a tree structure which mimics the DOM tree.
|
||||
|
||||
* View is a core rendering construct. A running application is just a collection of Views which are
|
||||
nested in a tree like structure. The View tree is a simplified version of the DOM tree. A View can
|
||||
have a single DOM Element or large DOM structures. The key is that the DOM tree in the View can
|
||||
not undergo structural changes (only property changes).
|
||||
* Views represent a running instance of a DOM View. This implies that while elements in a View
|
||||
can change properties, they can not change structurally. (Structural changes such as, adding or
|
||||
removing elements requires adding or removing child Views into ViewContainers).
|
||||
* View can have zero or more ViewContainers. A ViewContainer is a marker in the DOM which allows
|
||||
the insertion of child Views.
|
||||
* Views are created from a ProtoView. A ProtoView is a compiled DOM View which is efficient at
|
||||
creating Views.
|
||||
* View contains a context object. The context represents the object instance against which all
|
||||
expressions are evaluated.
|
||||
* View contains a ChangeDetector for looking for detecting changes to the model.
|
||||
* View contains ElementInjector for creating Directives.
|
||||
|
||||
## Simple View
|
||||
|
||||
Let's examine a simple View and all of its parts in detail.
|
||||
|
||||
Assume the following Component:
|
||||
|
||||
```
|
||||
class Greeter {
|
||||
greeting:string;
|
||||
|
||||
constructor() {
|
||||
this.greeting = 'Hello';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And assume following HTML View:
|
||||
|
||||
```
|
||||
<div>
|
||||
Your name:
|
||||
<input var="name" type="Text">
|
||||
<br>
|
||||
{{greeting}} {{name.value}}!
|
||||
</div>
|
||||
```
|
||||
|
||||
The above template is compiled by the Compiler to create a ProtoView. The ProtoView is then used to
|
||||
create an instance of the View. The instantiation process involves cloning the above template and
|
||||
locating all of the elements which contain bindings and finally instantiating the Directives
|
||||
associated with the template. (See compilation for more details.)
|
||||
|
||||
```
|
||||
<div> | viewA(greeter)
|
||||
Your name: | viewA(greeter)
|
||||
<input var="name" type="Text"> | viewA(greeter): local variable 'name'
|
||||
<br> | viewA(greeter)
|
||||
{{greeting}} {{name.value}}! | viewA(greeter): binding expression 'greeting' & 'name.value'
|
||||
</div> | viewA(greeter)
|
||||
```
|
||||
|
||||
The resulting View instance looks something like this (simplified pseudo code):
|
||||
```
|
||||
viewA = new View({
|
||||
template: ...,
|
||||
context: new Greeter(),
|
||||
localVars: ['name'],
|
||||
watchExp: ['greeting', 'name.value']
|
||||
});
|
||||
```
|
||||
|
||||
Note:
|
||||
* View uses instance of `Greeter` as the evaluation context.
|
||||
* View knows of local variables `name`.
|
||||
* View knows which expressions need to be watched.
|
||||
* View knows what needs to be updated if the watched expression changes.
|
||||
* All DOM elements are owned by single instance of the view.
|
||||
* The structure of the DOM can not change during runtime. To allow structural changes to the DOM we need
|
||||
to understand Composed View.
|
||||
|
||||
|
||||
## Composed View
|
||||
|
||||
An important part of an application is to be able to change the DOM structure to render data for the
|
||||
user. In Angular this is done by inserting child views into the ViewContainer.
|
||||
|
||||
Let's start with a View such as:
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor: let person of people">{{person}}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
During the compilation process the Compiler breaks the HTML template into these two ProtoViews:
|
||||
|
||||
```
|
||||
<li>{{person}}</li> | protoViewB(Locals)
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```
|
||||
<ul> | protoViewA(someContext)
|
||||
<template></template> | protoViewA(someContext): protoViewB
|
||||
</ul> | protoViewA(someContext)
|
||||
```
|
||||
|
||||
|
||||
The next step is to compose these two ProtoViews into an actual view which is rendered to the user.
|
||||
|
||||
*Step 1:* Instantiate `viewA`
|
||||
|
||||
```
|
||||
<ul> | viewA(someContext)
|
||||
<template></template> | viewA(someContext): new NgFor(new ViewContainer(protoViewB))
|
||||
</ul> | viewA(someContext)
|
||||
```
|
||||
|
||||
*Step2:* Instantiate `NgFor` directive which will receive the `ViewContainerRef`. (The ViewContainerRef
|
||||
has a reference to `protoViewA`).
|
||||
|
||||
|
||||
*Step3:* As the `NgFor` directive unrolls it asks the `ViewContainerRef` to instantiate `protoViewB` and insert
|
||||
it after the `ViewContainer` anchor. This is repeated for each `person` in `people`. Notice that
|
||||
|
||||
```
|
||||
<ul> | viewA(someContext)
|
||||
<template></template> | viewA(someContext): new NgFor(new ViewContainer(protoViewB))
|
||||
<li>{{person}}</li> | viewB0(locals0(someContext))
|
||||
<li>{{person}}</li> | viewB1(locals0(someContext))
|
||||
</ul> | viewA(someContext)
|
||||
```
|
||||
|
||||
*Step4:* All of the bindings in the child Views are updated. Notice that in the case of `NgFor`
|
||||
the evaluation context for the `viewB0` and `viewB1` are `locals0` and `locals1` respectively.
|
||||
Locals allow the introduction of new local variables visible only within the scope of the View, and
|
||||
delegate any unknown references to the parent context.
|
||||
|
||||
```
|
||||
<ul> | viewA
|
||||
<template></template> | viewA: new NgFor(new ViewContainer(protoViewB))
|
||||
<li>Alice</li> | viewB0
|
||||
<li>Bob</li> | viewB1
|
||||
</ul> | viewA
|
||||
```
|
||||
|
||||
Each View can have zero or more ViewContainers. By inserting and removing child Views to and from the
|
||||
ViewContainers, the application can mutate the DOM structure to any desirable state. A View may contain
|
||||
individual nodes or a complex DOM structure. The insertion points for the child Views, known as
|
||||
ViewContainers, contain a DOM element which acts as an anchor. The anchor is either a `template` or
|
||||
a `script` element depending on your browser. It is used to identify where the child Views will be
|
||||
inserted.
|
||||
|
||||
## Component Views
|
||||
|
||||
A View can also contain Components. Components contain Shadow DOM for encapsulating their internal
|
||||
rendering state. Unlike ViewContainers which can contain zero or more Views, the Component always contains
|
||||
exactly one Shadow View.
|
||||
|
||||
```
|
||||
<div> | viewA
|
||||
<my-component> | viewA
|
||||
#SHADOW_ROOT | (encapsulation boundary)
|
||||
<div> | viewB
|
||||
encapsulated rendering | viewB
|
||||
</div> | viewB
|
||||
</my-component> | viewA
|
||||
</div> | viewA
|
||||
```
|
||||
|
||||
## Evaluation Context
|
||||
|
||||
Each View acts as a context for evaluating its expressions. There are two kinds of contexts:
|
||||
|
||||
1. A component controller instance and
|
||||
2. a `Locals` context for introducing local variables into the View.
|
||||
|
||||
Let's assume following component:
|
||||
|
||||
```
|
||||
class Greeter {
|
||||
greeting:string;
|
||||
|
||||
constructor() {
|
||||
this.greeting = 'Hello';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And assume the following HTML View:
|
||||
|
||||
```
|
||||
<div> | viewA(greeter)
|
||||
Your name: | viewA(greeter)
|
||||
<input var="name" type="Text"> | viewA(greeter)
|
||||
<br> | viewA(greeter)
|
||||
{{greeting}} {{name.value}}! | viewA(greeter)
|
||||
</div> | viewA(greeter)
|
||||
```
|
||||
|
||||
The above UI is built using a single View, and hence a single context `greeter`. It can be expressed
|
||||
in this pseudo-code.
|
||||
|
||||
```
|
||||
var greeter = new Greeter();
|
||||
```
|
||||
|
||||
The View contains two bindings:
|
||||
|
||||
1. `greeting`: This is bound to the `greeting` property on the `Greeter` instance.
|
||||
2. `name.value`: This poses a problem. There is no `name` property on the `Greeter` instance. To solve
|
||||
this we wrap the `Greeter` instance in the `Local` instance like so:
|
||||
```
|
||||
var greeter = new Locals(new Greeter(), {name: ref_to_input_element })
|
||||
```
|
||||
|
||||
|
||||
By wrapping the `Greeter` instance into the `Locals` we allow the view to introduce variables which
|
||||
are in addition to the `Greeter` instance. During the resolution of the expressions we first check
|
||||
the locals, and then the `Greeter` instance.
|
||||
|
||||
|
||||
|
||||
## View LifeCycle (Hydration and Dehydration)
|
||||
|
||||
Views transition through a particular set of states:
|
||||
|
||||
1. View is created from the ProtoView.
|
||||
2. View can be attached to an existing ViewContainerRef.
|
||||
3. Upon attaching View to the ViewContainerRef the View needs to be hydrated. The hydration process
|
||||
involves instantiating all of the Directives associated with the current View.
|
||||
4. At this point the view is ready and renderable. Multiple changes can be delivered to the
|
||||
Directives from the ChangeDetection.
|
||||
5. At some point the View can be removed. At this point all of the directives are destroyed during
|
||||
the dehydration process and the view becomes inactive.
|
||||
6. The View has to wait until it is detached from the DOM. The delay in detaching could be caused
|
||||
because an animation is animating the view away.
|
||||
7. After the View is detached from the DOM it is ready to be reused. The view reuse allows the
|
||||
application to be faster in subsequent renderings.
|
@ -1,108 +0,0 @@
|
||||
# Zones
|
||||
|
||||
A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for
|
||||
JavaScript. Zones are used to intercept all async operation callbacks in the browser. By intercepting async
|
||||
callbacks Angular can automatically execute the change detection at the end of the VM turn to update the application
|
||||
UI bindings. Zones means that in Angular v2 you don't have to remember to call `rootScope.$apply()` in your async call.
|
||||
|
||||
## Execution Context
|
||||
|
||||
```
|
||||
zone.inTheZone = false;
|
||||
|
||||
zone.fork().run(function () {
|
||||
zone.inTheZone = true;
|
||||
|
||||
setTimeout(function () {
|
||||
console.log('async in the zone: ' + zone.inTheZone);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
console.log('sync in the zone: ' + zone.inTheZone);
|
||||
```
|
||||
|
||||
The above will log:
|
||||
|
||||
```
|
||||
sync in the zone: false
|
||||
async in the zone: true
|
||||
```
|
||||
|
||||
In the above example the `zone` is a global-callback-local variable. To the `zone` we can attach arbitrary properties
|
||||
such as `inTheZone`. When we enter the zone, we get a new `zone` context (which inherits all properties from the
|
||||
parent zone), once we leave the zone, the previous `zone` variable is restored. The key part is that when a async
|
||||
callback is scheduled in the zone, as is the case with `setTimeout`, the current `zone` variable is captured and
|
||||
restored on the callback. This is why the output of the `inTheZone` property inside the callback of the `setTimeout`
|
||||
prints `true`.
|
||||
|
||||
|
||||
## Callback Interception
|
||||
|
||||
In addition to storing properties on the current `zone`, zones can also allow us to intercept all async callbacks
|
||||
and notify us before and after the callback executes.
|
||||
|
||||
```
|
||||
zone.fork({
|
||||
afterTask: function () {
|
||||
// do some cleanup
|
||||
}
|
||||
}).run(function () {
|
||||
// do stuff
|
||||
});
|
||||
```
|
||||
|
||||
The above example will execute the `afterTask` function not only after the `run` finishes, but also after any callback
|
||||
execution which was registered in the `run` block.
|
||||
|
||||
## Putting it all together in Angular
|
||||
|
||||
In Angular2 it is not necessary to notify Angular of changes manually after async callback, because a relevant
|
||||
async callbacks are intercepted. The question is how do we know which callbacks are Angular relevant?
|
||||
|
||||
// TODO(vicb): outdated, rework.
|
||||
|
||||
```
|
||||
/// Some other code running on page can do async operation
|
||||
document.addEventListener('mousemove', function () {
|
||||
console.log('Mouse moved.');
|
||||
});
|
||||
|
||||
var AngularZone = {
|
||||
afterTask: function () {
|
||||
console.log('ANGULAR AUTO-DIGEST!');
|
||||
}
|
||||
};
|
||||
|
||||
var ngZone = zone.fork(AngularZone);
|
||||
ngZone.run(function() {
|
||||
console.log('I aware of angular, I should cause digest.');
|
||||
document.addEventListener('click', function () {
|
||||
console.log('Mouse clicked.');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Will produce:
|
||||
|
||||
```
|
||||
I aware of angular, I should cause digest.
|
||||
ANGULAR AUTO-DIGEST!
|
||||
```
|
||||
|
||||
Moving the mouse will produce many:
|
||||
```
|
||||
Mouse moved.
|
||||
```
|
||||
|
||||
But clicking will produce:
|
||||
```
|
||||
Mouse clicked.
|
||||
ANGULAR AUTO-DIGEST!
|
||||
```
|
||||
|
||||
Notice how the place where the listener was registered will effect whether or not Angular will be notified of the
|
||||
async call and cause a change detection to run to update the UI.
|
||||
|
||||
Being able to globally intercept the async operation is important to have a seamless integration with all existing
|
||||
libraries. But it is equally important to be able to differentiate between Angular and non-Angular code running
|
||||
on the same page concurrently.
|
@ -1,239 +0,0 @@
|
||||
Angular2 templates are now case-sensitive and use camelCase in many places where dash-case was previously used ([design doc] (https://docs.google.com/document/d/1UMxTIUBTIUZNfOqwMhkLg0ANFSBtLIK9JsIu77EZaBA/edit?ts=564f7dd4)).
|
||||
|
||||
## Overview
|
||||
|
||||
Where you used to write:
|
||||
|
||||
```
|
||||
<my-cmp (some-event)="someAction()" [some-property]="expression" #some-var>
|
||||
```
|
||||
|
||||
in order to:
|
||||
- bind to the `someEvent` event,
|
||||
- bind to the `someProperty` property,
|
||||
- create a `someVar` local variable
|
||||
|
||||
You should now write:
|
||||
|
||||
```
|
||||
<my-cmp (someEvent)="someAction()" [someProperty]="expression" #someVar>
|
||||
```
|
||||
|
||||
Notes:
|
||||
- while tag name are case sensitive, the best practice is to use dash case for component elements so that the browser
|
||||
interpret them as custom elements,
|
||||
- `(some-event)` would now bind to the `some-event` event (i.e. there is no implicit dash to camel case conversion),
|
||||
- `[some-property]` would now bind to the `some-property` property (i.e. there is no implicit dash to camel case conversion),
|
||||
- `#some-var` is not allowed any more ("-" can not be used in variable names).
|
||||
|
||||
## Migration
|
||||
|
||||
#### Templates
|
||||
|
||||
1. Directives selectors, property bindings, event bindings, template variables and template element attributes should be changed to camel case:
|
||||
|
||||
Examples:
|
||||
- `<p *ng-if="cond">` should be changed to `<p *ngIf="cond">`,
|
||||
- `<my-cmp [my-prop]="exp">` should be changed to `<my-cmp [myProp]="exp">`,
|
||||
- `<my-cmp (my-event)="action()">` should be changed to `<my-cmp (myEvent)="action()">`,
|
||||
- `<my-cmp [(my-prop)]="prop">` should be changed to `<my-cmp [(myProp)]="prop">`,
|
||||
- `<input #my-input>` should be changed to `<input #myInput>`,
|
||||
- `<template ng-for #my-item [ng-for-of]=items #my-index="index">` should be changed to `<template ngFor #myItem [ngForOf]=items #myIndex="index">`,
|
||||
|
||||
Note: while the tag names are now case-sensitive the best practice is to keep them lower-dash-cased so that the browser
|
||||
treat them as custom elements. Using dashes in custom element names is required by the [Custom Element HTML Spec](http://www.w3.org/TR/custom-elements/#concepts).
|
||||
This explains why the `<router-outlet>` component is left unchanged.
|
||||
|
||||
`on-`, `bindon-`, `bind-` and `var-` prefixes are still part of the canonical syntax and should remain unchanged (lower cased):
|
||||
- `on-some-event` should be changed to `on-someEvent`,
|
||||
- `bind-my-prop` should be changed to `bind-myProp`,
|
||||
- `bindon-my-prop` should be changed to `bindon-myProp`,
|
||||
- `var-my-var` should be changed to `var-myVar`.
|
||||
|
||||
2. Update variable binding
|
||||
|
||||
- `<p #var1="a-b" var-var2="c-d">` should be changed to `<p #var1="aB" var-var2="cD">`
|
||||
|
||||
3. The `template` attribute values should also be updated in the same way
|
||||
|
||||
`<p template="ng-for #my-item of items #my-index = index">` should be changed to `<p template="ngFor #myItem of items #myIndex = index">`.
|
||||
|
||||
Note that both angular directives and your own directives must be updated in the same way.
|
||||
|
||||
#### Directives and Components
|
||||
|
||||
Take the following steps to upgrade your directives and components:
|
||||
|
||||
1. Update the selector:
|
||||
```
|
||||
@Directive({selector: 'tag[attr][name=value]'})
|
||||
@Component({selector: 'tag[attr][name=value]'})
|
||||
```
|
||||
|
||||
Tag and attributes names are case sensitive:
|
||||
- For tag names, the best practice is to keep them lower dash cased, do not update them,
|
||||
- For attribute names, the best practice is to convert them from lower dash case to camel case.
|
||||
|
||||
Examples:
|
||||
- `custom-tag` should stay `custom-tag` (do not update tag names),
|
||||
- `[attr-name]` should be updated to `[attrName]`,
|
||||
- `[attr-name=someValue]` should be updated to `[attrName=someValue]`,
|
||||
- `custom-tag[attr-name=someValue]` should be updated to `custom-tag[attrName=someValue]`
|
||||
|
||||
Note: attribute values and classes are still matched case insensitive.
|
||||
|
||||
2. Update the inputs
|
||||
```
|
||||
@Directive({inputs: ['myProp', 'myOtherProp: my-attr-name']})
|
||||
```
|
||||
|
||||
As attribute names are now case sensitive, they should be converted from dash to camel case where they are specified.
|
||||
The previous decorator becomes:
|
||||
|
||||
```
|
||||
@Directive({inputs: ['myProp', 'myOtherProp: myAttrName']})
|
||||
```
|
||||
|
||||
Notes:
|
||||
- only the long syntax (with ":") needs to be updated,
|
||||
- `properties` is the legacy name for `inputs` and should be updated in the same way - it is a good idea to replace
|
||||
`properties` with `inputs` at the same time as support for the former will be removed soon.
|
||||
|
||||
The same applies for the `@Input` decorator:
|
||||
|
||||
```
|
||||
@Input() myProp;
|
||||
@Input('my-attr-name') myProp;
|
||||
```
|
||||
|
||||
That is they only need to be updated when the attribute name is specified:
|
||||
|
||||
```
|
||||
@Input() myProp; // Nothing to update
|
||||
@Input('myAttrName') myProp; // Convert the attribute name to camel case
|
||||
```
|
||||
|
||||
3. Update the outputs
|
||||
|
||||
Update the outputs in the same way the inputs are updated:
|
||||
|
||||
```
|
||||
@Directive({outputs: ['myEvent', 'myOtherEvent: my-event-name']})
|
||||
```
|
||||
|
||||
should be updated to:
|
||||
|
||||
```
|
||||
@Directive({outputs: ['myEvent', 'myOtherEvent: myEventName']})
|
||||
```
|
||||
|
||||
If you use `events` instead of `outputs` you should update in the same way and switch to `outputs` as `events` is deprecated.
|
||||
|
||||
```
|
||||
@Output() myEvent;
|
||||
@Output('my-event-name') myEvent;
|
||||
```
|
||||
|
||||
should be changed to:
|
||||
|
||||
```
|
||||
@Output() myEvent;
|
||||
@Output('myEventName') myEvent;
|
||||
```
|
||||
|
||||
4. Update the host bindings
|
||||
|
||||
```
|
||||
@Directive({
|
||||
host: {
|
||||
'[some-prop]': 'exp',
|
||||
'[style.background-color]': 'exp',
|
||||
'[class.some-class]': 'exp',
|
||||
'[attr.some-attr]': 'exp',
|
||||
'(some-event)': 'action()',
|
||||
'some-attr': 'value'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
should be changed to:
|
||||
|
||||
```
|
||||
@Directive({
|
||||
host: {
|
||||
'[someProp]': 'exp',
|
||||
'[style.background-color]': 'exp',
|
||||
'[class.some-class]': 'exp',
|
||||
'[attr.some-attr]': 'exp',
|
||||
'(someEvent)': 'action()',
|
||||
'some-attr': 'value'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The property bindings (`[...]`) and event bindings (`(...)`) must be updated in the same way as they are updated in a
|
||||
template - ie converted to camel case (reminder: `[attr.]`, `[class.]` and `[style.]` should not be converted to camel case).
|
||||
|
||||
5. Update export name
|
||||
|
||||
```
|
||||
@Directive({
|
||||
exportAs: 'some-name'
|
||||
})
|
||||
```
|
||||
|
||||
should be changed to:
|
||||
|
||||
```
|
||||
@Directive({
|
||||
exportAs: 'someName'
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
# CSS
|
||||
|
||||
As the attribute names from your templates have been updated from dash to camel case, you should also reflect the changes
|
||||
in your stylesheets.
|
||||
|
||||
The attributes that need to be updated are the ones used in the selector and the inputs of your directives.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
// Directive
|
||||
@Directive({
|
||||
selector: '[my-dir]',
|
||||
inputs: ['someProp: some-input'],
|
||||
})
|
||||
|
||||
<!-- template -->
|
||||
<div my-dir some-input="some value" not-an-input></div>
|
||||
|
||||
/* css */
|
||||
[my-dir] { ... }
|
||||
[some-input] { ... }
|
||||
[not-an-input] { ... }
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
// Directive
|
||||
@Directive({
|
||||
selector: '[myDir]',
|
||||
inputs: ['someProp: someInput'],
|
||||
})
|
||||
|
||||
<!-- template -->
|
||||
<div myDir someInput="some value" not-an-input></div>
|
||||
|
||||
/* css */
|
||||
[myDir] { ... }
|
||||
[someInput] { ... }
|
||||
[not-an-input] { ... }
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `not-an-input` is not used in a selector nor it is an input of a directive, it need not be camel cased,
|
||||
- CSS selectors are case insensitive you can use `[myDir]`, `[mydir]` or any other casing.
|
@ -55,20 +55,8 @@ export const REACTIVE_DRIVEN_DIRECTIVES: Type<any>[] =
|
||||
[FormControlDirective, FormGroupDirective, FormControlName, FormGroupName, FormArrayName];
|
||||
|
||||
/**
|
||||
* A list of all the form directives.
|
||||
*
|
||||
* A list of all the form directives used as part of a `@Component` annotation.
|
||||
*
|
||||
* This is a shorthand for importing them each individually.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* directives: [FORM_DIRECTIVES]
|
||||
* })
|
||||
* class MyApp {}
|
||||
* ```
|
||||
* @stable
|
||||
*/
|
||||
export const FORM_DIRECTIVES: Type<any>[][] = [TEMPLATE_DRIVEN_DIRECTIVES, SHARED_FORM_DIRECTIVES];
|
||||
@ -76,7 +64,6 @@ export const FORM_DIRECTIVES: Type<any>[][] = [TEMPLATE_DRIVEN_DIRECTIVES, SHARE
|
||||
/**
|
||||
* @stable
|
||||
*/
|
||||
|
||||
export const REACTIVE_FORM_DIRECTIVES: Type<any>[][] =
|
||||
[REACTIVE_DRIVEN_DIRECTIVES, SHARED_FORM_DIRECTIVES];
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user