docs(template-syntax/dart): updates to match TS (#2751)
* docs(template-syntax): refresh _cache * docs(template-syntax/dart): updates to match TS - Propagates TS-side changes: - update #2639 - new two-way binding section, and - fix #2687 - invalid attr syntax - Fixes - #1898 - currency symbols - #2748 - Dart template-syntax e2e is failing - #2749 - deprecated `[className]` * updated _cache file following Kathy's post-review edits * Post Ward's review w/ cache updated - Keep `my-` and `my` prefixes on selectors (for components and directives, respectively). - Drop `my-` from file names. - Drop `My` as component class prefix.
This commit is contained in:
		
							parent
							
								
									5dcffd69dc
								
							
						
					
					
						commit
						9e9666b2cc
					
				| @ -7,7 +7,8 @@ import 'package:angular2/common.dart'; | |||||||
| 
 | 
 | ||||||
| import 'hero.dart'; | import 'hero.dart'; | ||||||
| import 'hero_detail_component.dart'; | import 'hero_detail_component.dart'; | ||||||
| import 'my_click_directive.dart'; | import 'click_directive.dart'; | ||||||
|  | import 'sizer_component.dart'; | ||||||
| 
 | 
 | ||||||
| enum Color { red, green, blue } | enum Color { red, green, blue } | ||||||
| 
 | 
 | ||||||
| @ -18,7 +19,8 @@ enum Color { red, green, blue } | |||||||
|       HeroDetailComponent, |       HeroDetailComponent, | ||||||
|       BigHeroDetailComponent, |       BigHeroDetailComponent, | ||||||
|       MyClickDirective, |       MyClickDirective, | ||||||
|       MyClickDirective2 |       MyClickDirective2, | ||||||
|  |       MySizerComponent | ||||||
|     ]) |     ]) | ||||||
| class AppComponent implements OnInit, AfterViewInit { | class AppComponent implements OnInit, AfterViewInit { | ||||||
|   @override |   @override | ||||||
| @ -165,6 +167,7 @@ class AppComponent implements OnInit, AfterViewInit { | |||||||
|   bool isItalic = false; |   bool isItalic = false; | ||||||
|   bool isBold = false; |   bool isBold = false; | ||||||
|   String fontSize = 'large'; |   String fontSize = 'large'; | ||||||
|  |   String fontSizePx = '14'; | ||||||
| 
 | 
 | ||||||
|   Map<String, String> setStyle() { |   Map<String, String> setStyle() { | ||||||
|     return { |     return { | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| </div> | </div> | ||||||
| <br> | <br> | ||||||
| <a href="#event-binding">Event Binding</a><br> | <a href="#event-binding">Event Binding</a><br> | ||||||
| 
 | <a href="#two-way">Two-way Binding</a><br> | ||||||
| <br> | <br> | ||||||
| <div>Directives</div> | <div>Directives</div> | ||||||
| <div style="margin-left:8px"> | <div style="margin-left:8px"> | ||||||
| @ -242,9 +242,6 @@ button</button> | |||||||
| 
 | 
 | ||||||
|   <button [attr.disabled]="!isUnchanged">Disabled as well</button> |   <button [attr.disabled]="!isUnchanged">Disabled as well</button> | ||||||
| 
 | 
 | ||||||
|   <!-- can't remove it with [attr.disabled] either --> |  | ||||||
|   <button disabled [attr.disabled]>Still disabled</button> |  | ||||||
| 
 |  | ||||||
|   <!-- we'd have to remove it with property binding --> |   <!-- we'd have to remove it with property binding --> | ||||||
|   <button disabled [disabled]="false">Enabled (but inert)</button> |   <button disabled [disabled]="false">Enabled (but inert)</button> | ||||||
| </div> | </div> | ||||||
| @ -262,7 +259,7 @@ button</button> | |||||||
| <!-- #docregion class-binding-2 --> | <!-- #docregion class-binding-2 --> | ||||||
| <!-- reset/override all class names with a binding  --> | <!-- reset/override all class names with a binding  --> | ||||||
| <div class="bad curly special" | <div class="bad curly special" | ||||||
|      [className]="badCurly">Bad curly</div> |      [class]="badCurly">Bad curly</div> | ||||||
| <!-- #enddocregion class-binding-2 --> | <!-- #enddocregion class-binding-2 --> | ||||||
| 
 | 
 | ||||||
| <!-- #docregion class-binding-3 --> | <!-- #docregion class-binding-3 --> | ||||||
| @ -309,9 +306,9 @@ button</button> | |||||||
| <div> | <div> | ||||||
| <!-- #docregion event-binding-3 --> | <!-- #docregion event-binding-3 --> | ||||||
| <!-- `myClick` is an event on the custom `MyClickDirective` --> | <!-- `myClick` is an event on the custom `MyClickDirective` --> | ||||||
| <!-- #docregion my-click --> | <!-- #docregion myClick --> | ||||||
| <div (myClick)="clickMessage=$event">click with myClick</div> | <div (myClick)="clickMessage=$event">click with myClick</div> | ||||||
| <!-- #enddocregion my-click --> | <!-- #enddocregion myClick --> | ||||||
| <!-- #enddocregion event-binding-3 --> | <!-- #enddocregion event-binding-3 --> | ||||||
| {{clickMessage}} | {{clickMessage}} | ||||||
| </div> | </div> | ||||||
| @ -349,6 +346,24 @@ button</button> | |||||||
| </div> | </div> | ||||||
| <!-- #enddocregion event-binding-propagation --> | <!-- #enddocregion event-binding-propagation --> | ||||||
| <br><br> | <br><br> | ||||||
|  | <a class="to-toc" href="#toc">top</a> | ||||||
|  | 
 | ||||||
|  | <hr><h2 id="two-way">Two-way Binding</h2> | ||||||
|  | <div id="two-way-1"> | ||||||
|  |   <!-- #docregion two-way-1 --> | ||||||
|  |   <my-sizer [(size)]="fontSizePx"></my-sizer> | ||||||
|  |   <div [style.font-size.px]="fontSizePx">Resizable Text</div> | ||||||
|  |   <!-- #enddocregion two-way-1 --> | ||||||
|  |   <label>FontSize (px): <input [(ngModel)]="fontSizePx"></label> | ||||||
|  | </div> | ||||||
|  | <br> | ||||||
|  | <div id="two-way-2"> | ||||||
|  |   <h3>De-sugared two-way binding</h3> | ||||||
|  |   <!-- #docregion two-way-2 --> | ||||||
|  |   <my-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></my-sizer> | ||||||
|  |   <!-- #enddocregion two-way-2 --> | ||||||
|  | </div> | ||||||
|  | <br><br> | ||||||
| 
 | 
 | ||||||
| <a class="to-toc" href="#toc">top</a> | <a class="to-toc" href="#toc">top</a> | ||||||
| 
 | 
 | ||||||
| @ -746,7 +761,7 @@ bindon-ngModel | |||||||
| 
 | 
 | ||||||
| <div> | <div> | ||||||
|   <!-- pipe price to USD and display the $ symbol --> |   <!-- pipe price to USD and display the $ symbol --> | ||||||
|   <label>Price: </label>{{product['price'] | currency:'USD':false}} |   <label>Price: </label>{{product['price'] | currency:'USD':true}} | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| <a class="to-toc" href="#toc">top</a> | <a class="to-toc" href="#toc">top</a> | ||||||
|  | |||||||
| @ -5,11 +5,11 @@ import 'package:angular2/core.dart'; | |||||||
| 
 | 
 | ||||||
| @Directive(selector: '[myClick]') | @Directive(selector: '[myClick]') | ||||||
| class MyClickDirective { | class MyClickDirective { | ||||||
|   // #docregion my-click-output-1 |   // #docregion output-myClick | ||||||
|   // @Output(alias) [type info] propertyName = ... |   // @Output(alias) [type info] propertyName = ... | ||||||
|   @Output('myClick') final EventEmitter clicks = new EventEmitter<String>(); |   @Output('myClick') final EventEmitter clicks = new EventEmitter<String>(); | ||||||
| 
 | 
 | ||||||
|   // #enddocregion my-click-output-1 |   // #enddocregion output-myClick | ||||||
|   bool _toggle = false; |   bool _toggle = false; | ||||||
| 
 | 
 | ||||||
|   MyClickDirective(ElementRef el) { |   MyClickDirective(ElementRef el) { | ||||||
| @ -21,14 +21,14 @@ class MyClickDirective { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // #docregion my-click-output-2 | // #docregion output-myClick2 | ||||||
| @Directive( | @Directive( | ||||||
| // #enddocregion my-click-output-2 | // #enddocregion output-myClick2 | ||||||
|     selector: '[myClick2]', |     selector: '[myClick2]', | ||||||
| // #docregion my-click-output-2 | // #docregion output-myClick2 | ||||||
|     // ... |     // ... | ||||||
|     outputs: const ['clicks:myClick']) // propertyName:alias |     outputs: const ['clicks:myClick']) // propertyName:alias | ||||||
| // #enddocregion my-click-output-2 | // #enddocregion output-myClick2 | ||||||
| class MyClickDirective2 { | class MyClickDirective2 { | ||||||
|   final EventEmitter clicks = new EventEmitter<String>(); |   final EventEmitter clicks = new EventEmitter<String>(); | ||||||
|   bool _toggle = false; |   bool _toggle = false; | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | // #docregion | ||||||
|  | import 'dart:math'; | ||||||
|  | import 'package:angular2/core.dart'; | ||||||
|  | 
 | ||||||
|  | @Component( | ||||||
|  |     selector: 'my-sizer', | ||||||
|  |     template: ''' | ||||||
|  |       <div> | ||||||
|  |         <button (click)="dec()" title="smaller">-</button> | ||||||
|  |         <button (click)="inc()" title="bigger">+</button> | ||||||
|  |         <label [style.font-size.px]="size">FontSize: {{size}}px</label> | ||||||
|  |       </div>''') | ||||||
|  | class MySizerComponent { | ||||||
|  |   static final defaultPxSize = 14; | ||||||
|  | 
 | ||||||
|  |   @Input() | ||||||
|  |   String size; | ||||||
|  | 
 | ||||||
|  |   @Output() | ||||||
|  |   var sizeChange = new EventEmitter<String>(); | ||||||
|  | 
 | ||||||
|  |   void dec() => resize(-1); | ||||||
|  |   void inc() => resize(1); | ||||||
|  | 
 | ||||||
|  |   void resize(num delta) { | ||||||
|  |     final numSize = num.parse(size, (_) => defaultPxSize); | ||||||
|  |     size = min(40, max(8, numSize + delta)).toString(); | ||||||
|  |     sizeChange.emit(size); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -48,16 +48,6 @@ block dart-type-exception-example | |||||||
|       In checked mode, the code above will result in a type exception:  |       In checked mode, the code above will result in a type exception:  | ||||||
|       `String` isn't a subtype of `Hero`. |       `String` isn't a subtype of `Hero`. | ||||||
| 
 | 
 | ||||||
| block dart-class-binding-bug |  | ||||||
|   .callout.is-helpful |  | ||||||
|     header Angular Issue #6901 |  | ||||||
|     :marked |  | ||||||
|       Issue [#6901][6901] prevents us from using `[class]`. As is illustrated |  | ||||||
|       above, in the meantime we can achieve the same effect by binding to |  | ||||||
|       `className`. |  | ||||||
|        |  | ||||||
|       [6901]: http://github.com/angular/angular/issues/6901 |  | ||||||
| 
 |  | ||||||
| block style-property-name-dart-diff | block style-property-name-dart-diff | ||||||
|   .callout.is-helpful |   .callout.is-helpful | ||||||
|     header Dart difference: Style property names |     header Dart difference: Style property names | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ block includes | |||||||
|   * [Property binding](#property-binding) |   * [Property binding](#property-binding) | ||||||
|   * [Attribute, class, and style bindings](#other-bindings) |   * [Attribute, class, and style bindings](#other-bindings) | ||||||
|   * [Event binding](#event-binding) |   * [Event binding](#event-binding) | ||||||
|  |   * [Two-way data binding](#two-way) | ||||||
|   * [Two-way data binding with `NgModel`](#ngModel) |   * [Two-way data binding with `NgModel`](#ngModel) | ||||||
|   * [Built-in directives](#directives) |   * [Built-in directives](#directives) | ||||||
|     * [NgClass](#ngClass) |     * [NgClass](#ngClass) | ||||||
| @ -44,7 +45,7 @@ block includes | |||||||
|   HTML is the language of the Angular template. Our [QuickStart](../quickstart.html) application has a template that is pure HTML: |   HTML is the language of the Angular template. Our [QuickStart](../quickstart.html) application has a template that is pure HTML: | ||||||
| 
 | 
 | ||||||
| code-example(language="html" escape="html"). | code-example(language="html" escape="html"). | ||||||
|   <h1>My First Angular 2 App</h1> |   <h1>My First Angular App</h1> | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   Almost all HTML syntax is valid template syntax. The `<script>` element is a notable exception; it is forbidden, eliminating the risk of script injection attacks. (In practice, `<script>` is simply ignored.) |   Almost all HTML syntax is valid template syntax. The `<script>` element is a notable exception; it is forbidden, eliminating the risk of script injection attacks. (In practice, `<script>` is simply ignored.) | ||||||
| @ -387,7 +388,7 @@ table | |||||||
| .callout.is-helpful | .callout.is-helpful | ||||||
|   header A world without attributes |   header A world without attributes | ||||||
|   :marked |   :marked | ||||||
|     In the world of Angular 2, the only role of attributes is to initialize element and directive state. |     In the world of Angular, the only role of attributes is to initialize element and directive state. | ||||||
|     When we data bind, we're dealing exclusively with element and directive properties and events. |     When we data bind, we're dealing exclusively with element and directive properties and events. | ||||||
|     Attributes effectively disappear. |     Attributes effectively disappear. | ||||||
| :marked | :marked | ||||||
| @ -489,8 +490,8 @@ table | |||||||
|     If we must read a target element property or call one of its methods, |     If we must read a target element property or call one of its methods, | ||||||
|     we'll need a different technique. |     we'll need a different technique. | ||||||
|     See the API reference for |     See the API reference for | ||||||
|     [viewChild](../api/core/index/ViewChild-var.html) and |     [ViewChild](../api/core/index/ViewChild-decorator.html) and | ||||||
|     [contentChild](../api/core/index/ContentChild-var.html). |     [ContentChild](../api/core/index/ContentChild-decorator.html). | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   ### Binding target |   ### Binding target | ||||||
| @ -580,7 +581,7 @@ a(id="one-time-initialization") | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   #### Content Security |   #### Content security | ||||||
|   Imagine the following *malicious content*. |   Imagine the following *malicious content*. | ||||||
| +makeExample('template-syntax/ts/app/app.component.ts', 'evil-title')(format=".")     | +makeExample('template-syntax/ts/app/app.component.ts', 'evil-title')(format=".")     | ||||||
| :marked | :marked | ||||||
| @ -598,10 +599,10 @@ figure.image-display | |||||||
| .l-main-section | .l-main-section | ||||||
| :marked | :marked | ||||||
|   <a id="other-bindings"></a> |   <a id="other-bindings"></a> | ||||||
|   ## Attribute, Class, and Style Bindings |   ## Attribute, class, and style bindings | ||||||
|   The template syntax provides specialized one-way bindings for scenarios less well suited to property binding. |   The template syntax provides specialized one-way bindings for scenarios less well suited to property binding. | ||||||
| 
 | 
 | ||||||
|   ### Attribute Binding |   ### Attribute binding | ||||||
|   We can set the value of an attribute directly with an **attribute binding**. |   We can set the value of an attribute directly with an **attribute binding**. | ||||||
| .l-sub-section | .l-sub-section | ||||||
|   :marked |   :marked | ||||||
| @ -651,7 +652,7 @@ code-example(format="nocode"). | |||||||
|   is to set ARIA attributes, as in this example: |   is to set ARIA attributes, as in this example: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-aria')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-aria')(format=".") | ||||||
| :marked | :marked | ||||||
|   ### Class Binding |   ### Class binding | ||||||
| 
 | 
 | ||||||
|   We can add and remove CSS class names from an element’s `class` attribute with |   We can add and remove CSS class names from an element’s `class` attribute with | ||||||
|   a **class binding**. |   a **class binding**. | ||||||
| @ -667,9 +668,6 @@ code-example(format="nocode"). | |||||||
|   We can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding. |   We can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding. | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-2')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-2')(format=".") | ||||||
| 
 | 
 | ||||||
| block dart-class-binding-bug |  | ||||||
|   //- N/A |  | ||||||
| 
 |  | ||||||
| :marked | :marked | ||||||
|   Finally, we can bind to a specific class name. |   Finally, we can bind to a specific class name. | ||||||
|   Angular adds the class when the template expression evaluates to #{_truthy}. |   Angular adds the class when the template expression evaluates to #{_truthy}. | ||||||
| @ -682,7 +680,7 @@ block dart-class-binding-bug | |||||||
|     we generally prefer the [NgClass directive](#ngClass) for managing multiple class names at the same time. |     we generally prefer the [NgClass directive](#ngClass) for managing multiple class names at the same time. | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   ### Style Binding |   ### Style binding | ||||||
| 
 | 
 | ||||||
|   We can set inline styles with a **style binding**. |   We can set inline styles with a **style binding**. | ||||||
| 
 | 
 | ||||||
| @ -711,12 +709,12 @@ block style-property-name-dart-diff | |||||||
| 
 | 
 | ||||||
| .l-main-section | .l-main-section | ||||||
| :marked | :marked | ||||||
|   ## Event Binding |   ## Event binding | ||||||
|   The bindings we’ve met so far flow data in one direction: *from the component to an element*. |   The bindings we’ve met so far flow data in one direction: **from a component to an element**. | ||||||
| 
 | 
 | ||||||
|   Users don’t just stare at the screen. They enter text into input boxes. They pick items from lists. |   Users don’t just stare at the screen. They enter text into input boxes. They pick items from lists. | ||||||
|   They click buttons. Such user actions may result in a flow of data in the opposite direction: |   They click buttons. Such user actions may result in a flow of data in the opposite direction: | ||||||
|   *from an element to the component*. |   **from an element to a component**. | ||||||
| 
 | 
 | ||||||
|   The only way to know about a user action is to listen for certain events such as |   The only way to know about a user action is to listen for certain events such as | ||||||
|   keystrokes, mouse movements, clicks, and touches. |   keystrokes, mouse movements, clicks, and touches. | ||||||
| @ -728,12 +726,12 @@ block style-property-name-dart-diff | |||||||
|   the component's `onSave()` method whenever a click occurs: |   the component's `onSave()` method whenever a click occurs: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") | ||||||
| :marked | :marked | ||||||
|   ### Target Event |   ### Target event | ||||||
|   A **name between enclosing parentheses** — for example, `(click)` — |   A **name between parentheses** — for example, `(click)` — | ||||||
|   identifies the target event. In the following example, the target is the button’s click event. |   identifies the target event. In the following example, the target is the button’s click event. | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") | ||||||
| :marked | :marked | ||||||
|   Some people prefer the `on-` prefix alternative, known as the *canonical form*: |   Some people prefer the `on-` prefix alternative, known as the **canonical form**: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-2')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-2')(format=".") | ||||||
| :marked | :marked | ||||||
|   Element events may be the more common targets, but Angular looks first to see if the name matches an event property |   Element events may be the more common targets, but Angular looks first to see if the name matches an event property | ||||||
| @ -742,8 +740,8 @@ block style-property-name-dart-diff | |||||||
| 
 | 
 | ||||||
| .l-sub-section | .l-sub-section | ||||||
|   :marked |   :marked | ||||||
|     The `myClick` directive is further described below in the section |     The `myClick` directive is further described in the section | ||||||
|     on [Aliasing input/output properties](#aliasing-io). |     on [aliasing input/output properties](#aliasing-io). | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   If the name fails to match an element event or an output property of a known directive, |   If the name fails to match an element event or an output property of a known directive, | ||||||
| @ -753,36 +751,35 @@ block style-property-name-dart-diff | |||||||
|   In an event binding, Angular sets up an event handler for the target event. |   In an event binding, Angular sets up an event handler for the target event. | ||||||
| 
 | 
 | ||||||
|   When the event is raised, the handler executes the template statement. |   When the event is raised, the handler executes the template statement. | ||||||
|   The template statement typically involves a receiver that wants to do something |   The template statement typically involves a receiver, which performs an action | ||||||
|   in response to the event, such as take a value from the HTML control and store it |   in response to the event, such as storing a value from the HTML control | ||||||
|   in a model. |   into a model. | ||||||
| 
 | 
 | ||||||
|   The binding conveys information about the event, including data values, through |   The binding conveys information about the event, including data values, through | ||||||
|   an **event object named `$event`**. |   an **event object named `$event`**. | ||||||
| 
 | 
 | ||||||
|   The shape of the event object is determined by the target event itself. |   The shape of the event object is determined by the target event. | ||||||
|   If the target event is a native DOM element event, the `$event` is a |   If the target event is a native DOM element event, then `$event` is a | ||||||
|   [DOM event object]( https://developer.mozilla.org/en-US/docs/Web/Events), |   [DOM event object]( https://developer.mozilla.org/en-US/docs/Web/Events), | ||||||
|   with properties such as `target` and `target.value`. |   with properties such as `target` and `target.value`. | ||||||
| 
 | 
 | ||||||
|   Consider this example: |   Consider this example: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".") | ||||||
| :marked | :marked | ||||||
|   We’re binding the input box `value` to a `firstName` property, and we’re listening for changes by binding to the input box’s `input` event. |   This code sets the input box `value` property by binding to the `firstName` property. To listen for changes to the value, the code binds to the input box's `input` event. | ||||||
|   When the user makes changes, the `input` event is raised, and the binding executes the statement within a context that includes the DOM event object, `$event`. |   When the user makes changes, the `input` event is raised, and the binding executes the statement within a context that includes the DOM event object, `$event`. | ||||||
| 
 | 
 | ||||||
|   To update the `firstName` property, we must get the changed text by following |   To update the `firstName` property, the changed text is retrieved by following the path `$event.target.value`. | ||||||
|   the path `$event.target.value`. |  | ||||||
| 
 | 
 | ||||||
|   If the event belongs to a directive (remember: components are directives), `$event` has whatever shape the directive chose to produce. |   If the event belongs to a directive (recall that components are directives), `$event` has whatever shape the directive decides to produce. | ||||||
| 
 | 
 | ||||||
|   <a id="eventemitter"></a> |   <a id="eventemitter"></a> | ||||||
|   <a id="custom-event"></a> |   <a id="custom-event"></a> | ||||||
|   ### Custom Events with EventEmitter |   ### Custom events with *EventEmitter* | ||||||
| 
 | 
 | ||||||
|   Directives typically raise custom events with an Angular [EventEmitter](../api/core/index/EventEmitter-class.html). |   Directives typically raise custom events with an Angular [EventEmitter](../api/core/index/EventEmitter-class.html). | ||||||
|   A directive creates an `EventEmitter` and exposes it as a property. |   The directive creates an `EventEmitter` and exposes it as a property. | ||||||
|   The directive calls `EventEmitter.emit(payload)` to fire an event, passing in a message payload that can be anything. |   The directive calls `EventEmitter.emit(payload)` to fire an event, passing in a message payload, which can be anything. | ||||||
|   Parent directives listen for the event by binding to this property and accessing the payload through the `$event` object. |   Parent directives listen for the event by binding to this property and accessing the payload through the `$event` object. | ||||||
| 
 | 
 | ||||||
|   Consider a `HeroDetailComponent` that presents hero information and responds to user actions. |   Consider a `HeroDetailComponent` that presents hero information and responds to user actions. | ||||||
| @ -797,8 +794,8 @@ block style-property-name-dart-diff | |||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   The component defines a `deleteRequest` property that returns an `EventEmitter`. |   The component defines a `deleteRequest` property that returns an `EventEmitter`. | ||||||
|   When the user clicks *delete*, the component invokes the `delete()` method |   When the user clicks *delete*, the component invokes the `delete()` method,  | ||||||
|   which tells the `EventEmitter` to emit a `Hero` object. |   telling the `EventEmitter` to emit a `Hero` object. | ||||||
| 
 | 
 | ||||||
|   Now imagine a hosting parent component that binds to the `HeroDetailComponent`'s `deleteRequest` event. |   Now imagine a hosting parent component that binds to the `HeroDetailComponent`'s `deleteRequest` event. | ||||||
| 
 | 
 | ||||||
| @ -810,12 +807,11 @@ block style-property-name-dart-diff | |||||||
| 
 | 
 | ||||||
|   ### Template statements have side effects |   ### Template statements have side effects | ||||||
|   The `deleteHero` method has a side effect: it deletes a hero. |   The `deleteHero` method has a side effect: it deletes a hero. | ||||||
|   Template statement side effects are not just OK, they are expected. |   Template statement side effects are not just OK, but expected. | ||||||
| 
 | 
 | ||||||
|   Deleting the hero updates the model, perhaps triggering other changes |   Deleting the hero updates the model, perhaps triggering other changes | ||||||
|   including queries and saves to a remote server. |   including queries and saves to a remote server. | ||||||
|   These changes percolate through the system and are ultimately displayed in this and other views. |   These changes percolate through the system and are ultimately displayed in this and other views. | ||||||
|   It's all good. |  | ||||||
| 
 | 
 | ||||||
| // | // | ||||||
|   :marked |   :marked | ||||||
| @ -843,19 +839,72 @@ block style-property-name-dart-diff | |||||||
|     and the outer `<div>`, causing a double save. |     and the outer `<div>`, causing a double save. | ||||||
|   +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-propagation')(format=".") |   +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-propagation')(format=".") | ||||||
| 
 | 
 | ||||||
| 
 | #two-way | ||||||
|  | .l-main-section | ||||||
|  | :marked | ||||||
|  |   ## Two-way binding | ||||||
|  |   We often want to both display a data property and update that property when the user makes changes. | ||||||
|  | 
 | ||||||
|  |   On the element side that takes a combination of setting a specific element property | ||||||
|  |   and listening for an element change event. | ||||||
|  | 
 | ||||||
|  |   Angular offers a special _two-way data binding_ syntax for this purpose, **`[(x)]`**. | ||||||
|  |   The `[(x)]` syntax combines the brackets  | ||||||
|  |   of _property binding_, `[x]`, with the parentheses of _event binding_, `(x)`. | ||||||
|  | 
 | ||||||
|  | .callout.is-important | ||||||
|  |   header [( )] = banana in a box | ||||||
|  |   :marked | ||||||
|  |     Visualize a *banana in a box* to remember that the parentheses go _inside_ the brackets. | ||||||
|  | 
 | ||||||
|  | :marked | ||||||
|  |   The `[(x)]` syntax is easy to demonstrate when the element has a settable property called `x` | ||||||
|  |   and a corresponding event named `xChange`.  | ||||||
|  |   Here's a `SizerComponent` that fits the pattern. | ||||||
|  |   It has a `size` value property and a companion `sizeChange` event: | ||||||
|  | 
 | ||||||
|  | +makeExample('app/sizer.component.ts') | ||||||
|  | 
 | ||||||
|  | :marked | ||||||
|  |   The initial `size` is an input value from a property binding. | ||||||
|  |   Clicking the buttons increases or decreases the `size`, within min/max values constraints, | ||||||
|  |   and then raises (_emits_) the `sizeChange` event with the adjusted size. | ||||||
|  | 
 | ||||||
|  |   Here's an example in which the `AppComponent.fontSizePx` is two-way bound to the `SizerComponent`: | ||||||
|  | 
 | ||||||
|  | +makeExcerpt('app/app.component.html', 'two-way-1', '') | ||||||
|  | 
 | ||||||
|  | :marked | ||||||
|  |   The `AppComponent.fontSizePx` establishes the initial `SizerComponent.size` value. | ||||||
|  |   Clicking the buttons updates the `AppComponent.fontSizePx` via the two-way binding. | ||||||
|  |   The revised `AppComponent.fontSizePx` value flows through to the _style_ binding, making the displayed text bigger or smaller. | ||||||
|  |   Try it in the <live-example></live-example>. | ||||||
|  | 
 | ||||||
|  |   The two-way binding syntax is really just syntactic sugar for a _property_ binding and an _event_ binding. | ||||||
|  |   Angular _desugars_ the `SizerComponent` binding into this: | ||||||
|  | 
 | ||||||
|  | +makeExcerpt('app/app.component.html', 'two-way-2', '') | ||||||
|  | 
 | ||||||
|  | :marked | ||||||
|  |   The `$event` variable contains the payload of the `SizerComponent.sizeChange` event. | ||||||
|  |   Angular assigns the `$event` value to the `AppComponent.fontSizePx` when the user clicks the buttons. | ||||||
|  | 
 | ||||||
|  |   Clearly the two-way binding syntax is a great convenience compared to separate property and event bindings. | ||||||
|  | 
 | ||||||
|  |   We'd like to use two-way binding with HTML form elements like `<input>` and `<select>`. | ||||||
|  |   Sadly, no native HTML element follows the `x` value and `xChange` event pattern. | ||||||
|  | 
 | ||||||
|  |   Fortunately, the Angular [_NgModel_](#ngModel) directive is a bridge that enables two-way binding to form elements. | ||||||
|  | 
 | ||||||
|  | a#ngModel | ||||||
| .l-main-section | .l-main-section | ||||||
| :marked | :marked | ||||||
|   <a id="ngModel"></a> |  | ||||||
|   ## Two-way binding with NgModel |   ## Two-way binding with NgModel | ||||||
|   When developing data entry forms, we often want to both display a data property and update that property when the user makes changes. |   When developing data entry forms, we often want to both display a data property and update that property when the user makes changes. | ||||||
| 
 | 
 | ||||||
|   The `[(ngModel)]` two-way data binding syntax makes that easy. Here's an example: |   Two-way data binding with the `NgModel` directive makes that easy. Here's an example: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".") | ||||||
| .callout.is-important | 
 | ||||||
|   header [()] = banana in a box |  | ||||||
|   :marked |  | ||||||
|     To remember that the parentheses go inside the brackets, visualize a *banana in a box*. |  | ||||||
| 
 | 
 | ||||||
| +ifDocsFor('ts|js') | +ifDocsFor('ts|js') | ||||||
|   .callout.is-important |   .callout.is-important | ||||||
| @ -865,18 +914,18 @@ block style-property-name-dart-diff | |||||||
|       we must import the `FormsModule` and add it to the Angular module's `imports` list. |       we must import the `FormsModule` and add it to the Angular module's `imports` list. | ||||||
|       Learn more about the `FormsModule` and `ngModel` in the |       Learn more about the `FormsModule` and `ngModel` in the | ||||||
|       [Forms](../guide/forms.html#ngModel) chapter. |       [Forms](../guide/forms.html#ngModel) chapter. | ||||||
| 
 |   :marked | ||||||
|  |     Here's how to import the `FormsModule` to make `[(ngModel)]` available. | ||||||
|   +makeExample('template-syntax/ts/app/app.module.1.ts', '', 'app.module.ts (FormsModule import)') |   +makeExample('template-syntax/ts/app/app.module.1.ts', '', 'app.module.ts (FormsModule import)') | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   There’s a story behind this construction, a story that builds on the property and event binding techniques we learned previously. |  | ||||||
| 
 |  | ||||||
|   ### Inside `[(ngModel)]` |   ### Inside `[(ngModel)]` | ||||||
|   We could have achieved the same result with separate bindings to |   Looking back at the `firstName` binding, it's important to note that | ||||||
|  |   we could have achieved the same result with separate bindings to | ||||||
|   the `<input>` element's  `value` property and `input` event. |   the `<input>` element's  `value` property and `input` event. | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".") | ||||||
| :marked | :marked | ||||||
|   That’s cumbersome. Who can remember which element property to set and what event reports user changes? |   That’s cumbersome. Who can remember which element property to set and which element event emits user changes? | ||||||
|   How do we extract the currently displayed text from the input box so we can update the data property? |   How do we extract the currently displayed text from the input box so we can update the data property? | ||||||
|   Who wants to look that up each time? |   Who wants to look that up each time? | ||||||
| 
 | 
 | ||||||
| @ -884,35 +933,29 @@ block style-property-name-dart-diff | |||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-3')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-3')(format=".") | ||||||
| .l-sub-section | .l-sub-section | ||||||
|   :marked |   :marked | ||||||
|     The `ngModel` input property sets the element's value property and the `ngModelChange` output property |     The `ngModel` data property sets the element's value property and the `ngModelChange` event property | ||||||
|     listens for changes to the element's value. |     listens for changes to the element's value. | ||||||
|     The details are specific to each kind of element and therefore the `NgModel` directive only works for elements, |      | ||||||
|     such as the input text box, that are supported by a [ControlValueAccessor](../api/common/index/ControlValueAccessor-interface.html). |     The details are specific to each kind of element and therefore the `NgModel` directive only works for specific form elements, | ||||||
|     We can't apply `[(ngModel)]` to our custom components until we write a suitable *value accessor*, |     such as the input text box, that are supported by a [ControlValueAccessor](../api/forms/index/ControlValueAccessor-interface.html). | ||||||
|  | 
 | ||||||
|  |     We can't apply `[(ngModel)]` to a custom component until we write a suitable *value accessor*, | ||||||
|     a technique that is beyond the scope of this chapter. |     a technique that is beyond the scope of this chapter. | ||||||
|  |     That's something we might want to do for an Angular component or a WebComponent whose API we can't control. | ||||||
|  |      | ||||||
|  |     It's completely unnecessary for an Angular component that we _do_ control ... because we can name the value and event properties | ||||||
|  |     to suit Angular's basic [two-way binding syntax](#two-way) and skip `NgModel` altogether. | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   Separate `ngModel` bindings is an improvement. We can do better. |   Separate `ngModel` bindings is an improvement over binding to the element's native properties. We can do better. | ||||||
| 
 | 
 | ||||||
|   We shouldn't have to mention the data property twice. Angular should be able to capture the component’s data property and set it |   We shouldn't have to mention the data property twice. Angular should be able to capture the component’s data property and set it | ||||||
|   with a single declaration — which it can with the `[( )]` syntax: |   with a single declaration — which it can with the `[(ngModel)]` syntax: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".") | ||||||
| 
 |  | ||||||
| .l-sub-section |  | ||||||
|   :marked |  | ||||||
|     `[(ngModel)]` is a specific example of a more general pattern in which Angular "de-sugars" the `[(x)]` syntax |  | ||||||
|     into an `x` input property for property binding and an `xChange` output property for event binding. |  | ||||||
|     Angular constructs the event property binding's template statement by appending `=$event` |  | ||||||
|     to the literal string of the template expression. |  | ||||||
| 
 |  | ||||||
|     > <span style="font-family:courier">[(_x_)]="_e_" <==> [_x_]="_e_" (<i>x</i>Change)="_e_=$event"</span> |  | ||||||
| 
 |  | ||||||
|     We can write a two-way binding directive of our own to exploit this behavior. |  | ||||||
| 
 |  | ||||||
| :marked | :marked | ||||||
|   Is `[(ngModel)]` all we need? Is there ever a reason to fall back to its expanded form? |   Is `[(ngModel)]` all we need? Is there ever a reason to fall back to its expanded form? | ||||||
| 
 | 
 | ||||||
|   The `[( )]` syntax can only _set_ a data-bound property. |   The `[(ngModel)]` syntax can only _set_ a data-bound property. | ||||||
|   If we need to do something more or something different, we need to write the expanded form ourselves. |   If we need to do something more or something different, we need to write the expanded form ourselves. | ||||||
| 
 | 
 | ||||||
|   Let's try something silly like forcing the input value to uppercase: |   Let's try something silly like forcing the input value to uppercase: | ||||||
| @ -931,8 +974,8 @@ figure.image-display | |||||||
|   The community contributed many more, and countless private directives |   The community contributed many more, and countless private directives | ||||||
|   have been created for internal applications. |   have been created for internal applications. | ||||||
| 
 | 
 | ||||||
|   We don’t need many of those directives in Angular 2. |   We don’t need many of those directives in Angular. | ||||||
|   Quite often we can achieve the same results with the more capable and expressive Angular 2 binding system. |   Quite often we can achieve the same results with the more capable and expressive Angular binding system. | ||||||
|   Why create a directive to handle a click when we can write a simple binding such as this? |   Why create a directive to handle a click when we can write a simple binding such as this? | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") | ||||||
| :marked | :marked | ||||||
| @ -1203,7 +1246,7 @@ block remember-the-brackets | |||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   ### Expanding `*ngSwitch` |   ### Expanding `*ngSwitch` | ||||||
|   A similar transformation applies to `*ngSwitch`. We can de-sugar the syntax ourselves. |   A similar transformation applies to `*ngSwitch`. We can unfold the syntax ourselves. | ||||||
|   Here's an example, first with `*ngSwitchCase` and `*ngSwitchDefault` and then again with `<template>` tags: |   Here's an example, first with `*ngSwitchCase` and `*ngSwitchDefault` and then again with `<template>` tags: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch-expanded')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch-expanded')(format=".") | ||||||
| :marked | :marked | ||||||
| @ -1241,14 +1284,18 @@ figure.image-display | |||||||
| 
 | 
 | ||||||
|   A **template reference variable** is a reference to a DOM element or directive within a template. |   A **template reference variable** is a reference to a DOM element or directive within a template. | ||||||
| 
 | 
 | ||||||
|   It can be used with native DOM elements but also with Angular 2 components — in fact, it will work with any custom web component. |   It can be used with native DOM elements but also with Angular components — in fact, it will work with any custom web component. | ||||||
| 
 | 
 | ||||||
| :marked | :marked | ||||||
|   ### Referencing a template reference variable |   ### Referencing a template reference variable | ||||||
| 
 | 
 | ||||||
|   We can reference a template reference variable on the same element, on a sibling element, or on |   We can refer to a template reference variable _anywhere_ in the current template. | ||||||
|   any child elements. | .l-sub-section | ||||||
|  |   :marked | ||||||
|  |     Do not define the same variable name more than once in the same template. | ||||||
|  |     The runtime value will be unpredictable. | ||||||
| 
 | 
 | ||||||
|  | :marked | ||||||
|   Here are two other examples of creating and consuming a Template reference variable: |   Here are two other examples of creating and consuming a Template reference variable: | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'ref-phone')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'ref-phone')(format=".") | ||||||
| :marked | :marked | ||||||
| @ -1376,7 +1423,7 @@ h3#aliasing-io Aliasing input/output properties | |||||||
|   Directive consumers expect to bind to the name of the directive. |   Directive consumers expect to bind to the name of the directive. | ||||||
|   For example, when we apply a directive with a `myClick` selector to a `<div>` tag, |   For example, when we apply a directive with a `myClick` selector to a `<div>` tag, | ||||||
|   we expect to bind to an event property that is also called `myClick`. |   we expect to bind to an event property that is also called `myClick`. | ||||||
| +makeExample('template-syntax/ts/app/app.component.html', 'my-click')(format=".") | +makeExample('template-syntax/ts/app/app.component.html', 'myClick')(format=".") | ||||||
| :marked | :marked | ||||||
|   However, the directive name is often a poor choice for the name of a property within the directive class. |   However, the directive name is often a poor choice for the name of a property within the directive class. | ||||||
|   The directive name rarely describes what the property does. |   The directive name rarely describes what the property does. | ||||||
| @ -1389,14 +1436,14 @@ h3#aliasing-io Aliasing input/output properties | |||||||
| 
 | 
 | ||||||
|   We can specify the alias for the property name by passing it into the input/output decorator like this: |   We can specify the alias for the property name by passing it into the input/output decorator like this: | ||||||
| 
 | 
 | ||||||
| +makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-1')(format=".") | +makeExample('template-syntax/ts/app/click.directive.ts', 'output-myClick')(format=".") | ||||||
| 
 | 
 | ||||||
| .l-sub-section | .l-sub-section | ||||||
|   :marked |   :marked | ||||||
|     We can also alias property names in the `inputs` and `outputs` #{_array}s. |     We can also alias property names in the `inputs` and `outputs` #{_array}s. | ||||||
|     We write a colon-delimited (`:`) string with |     We write a colon-delimited (`:`) string with | ||||||
|     the directive property name on the *left* and the public alias on the *right*: |     the directive property name on the *left* and the public alias on the *right*: | ||||||
|   +makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-2')(format=".") |   +makeExample('template-syntax/ts/app/click.directive.ts', 'output-myClick2')(format=".") | ||||||
| 
 | 
 | ||||||
| <a id="expression-operators"></a> | <a id="expression-operators"></a> | ||||||
| .l-main-section | .l-main-section | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user