fix(template syntax): minor fixes

closes #535
This commit is contained in:
Victor Berchet 2015-12-15 14:10:10 -08:00 committed by Ward Bell
parent a89e57f1b1
commit 933433ceea
1 changed files with 78 additions and 82 deletions

View File

@ -35,7 +35,7 @@ include ../../../../_includes/_util-fns
>[Input and Output Properties](#inputs-outputs)
>[Template Expression Operators](#expression-operators)
[Live Example](/resources/live-examples/template-syntax/ts/plnkr.html).
.l-main-section
@ -118,7 +118,7 @@ include ../../../../_includes/_util-fns
The component itself is usually the expression *context* in which case
the template expression usually references that component.
The expression context may include an object other than the component.
A [local template variable](#local-vars) is one such supplemental context object;
well discuss that option below.
@ -192,7 +192,7 @@ table
But its also significantly different than the HTML were used to.
We really need a new mental model.
In the normal course of HTML development, we create a visual structure with HTML elements and
In the normal course of HTML development, we create a visual structure with HTML elements and
we modify those elements by setting element attributes with string constants.
+makeExample('template-syntax/ts/app/app.component.html', 'img+button')(format=".")
:marked
@ -330,10 +330,6 @@ table
+makeExample('template-syntax/ts/app/app.component.html', 'style-binding-syntax-1')(format=".")
</div>
.l-sub-section
:marked
According to this rule, we should write `[inner-h-t-m-l]` to access the elements `innerHTML` property.
Fortunately, the Angular template parser recognizes `inner-html` as an acceptable alias for `innerHTML`.
:marked
Lets descend from the architectural clouds and look at each of these binding types in concrete detail.
@ -407,7 +403,7 @@ table
### Property Binding or Interpolation?
We often have a choice between Interpolation and Property Binding. The following binding pairs do the same thing
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".")
:marked
Interpolation is actually a convenient alternative for Property Binding in many cases.
@ -452,7 +448,7 @@ code-example(format="", language="html").
Can't bind to 'colspan' since it isn't a known native property
:marked
As the message says, the `<td>` element does not have a `colspan` property.
It has the "colspan" *attribute* but
It has the "colspan" *attribute* but
interpolation and property binding can only set properties, not attributes.
We need an Attribute Binding to create and bind to such attributes.
@ -466,7 +462,7 @@ code-example(format="", language="html").
:marked
Here's how the table renders:
<table border="1px">
<tr><td colspan="2">One-Two</td></tr>
<tr><td colspan="2">One-Two</td></tr>
<tr><td>Five</td><td>Six</td></tr>
</table>
@ -475,11 +471,11 @@ code-example(format="", language="html").
+makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-aria')(format=".")
:marked
### Class Binding
We can add and remove CSS class names from an elements `class` attribute with the **Class Binding**.
Class Binding syntax resembles Property Binding.
Instead of an element property between brackets, we start with the keyword `class` followed
Class Binding syntax resembles Property Binding.
Instead of an element property between brackets, we start with the keyword `class` followed
by the name of a CSS class: `[class.class-name]`.
In the following examples we see how to add and remove the application's "special" class
@ -490,7 +486,7 @@ code-example(format="", language="html").
+makeExample('template-syntax/ts/app/app.component.html', 'class-binding-2')(format=".")
:marked
Finally, we bind to a specific class name.
Angular adds the class when the template expression evaluates to something truthy and
Angular adds the class when the template expression evaluates to something truthy and
removes the class name when the expression is falsey.
+makeExample('template-syntax/ts/app/app.component.html', 'class-binding-3')(format=".")
@ -504,8 +500,8 @@ code-example(format="", language="html").
We can set inline styles with a **Style Binding**.
Style Binding syntax resembles Property Binding.
Instead of an element property between brackets, we start with the key word `style`
Style Binding syntax resembles Property Binding.
Instead of an element property between brackets, we start with the key word `style`
followed by the name of a CSS style property: `[style.style-property]`.
+makeExample('template-syntax/ts/app/app.component.html', 'style-binding-1')(format=".")
@ -547,7 +543,7 @@ code-example(format="", language="html").
of a known directive as it does in the following example:
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-3')(format=".")
: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,
Angular reports an “unknown directive” error.
### $event and event handling expressions
@ -568,16 +564,16 @@ code-example(format="", language="html").
Consider this example:
+makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".")
:marked
Were binding the input box `value` to a `firstName` property and were listening for changes by binding to the input boxs `input` event.
When the user makes changes, the `input` event is raised, and the binding executes the expression within a context that includes the DOM event object, `$event`.
Were binding the input box `value` to a `firstName` property and were listening for changes by binding to the input boxs `input` event.
When the user makes changes, the `input` event is raised, and the binding executes the expression within a context that includes the DOM event object, `$event`.
We must follow the `$event.target.value` path to get the changed text so we can update the `firstName`
If the event belongs to a directive (remember: components are directives), `$event` has whatever shape the directive chose to produce.
Consider a `HeroDetailComponent` that produces `deleted` events with an `EventEmitter`.
If the event belongs to a directive (remember: components are directives), `$event` has whatever shape the directive chose to produce.
Consider a `HeroDetailComponent` that produces `deleted` events with an `EventEmitter`.
+makeExample('template-syntax/ts/app/hero-detail.component.ts', 'deleted', 'HeroDetailComponent.ts (excerpt)')(format=".")
:marked
When something invokes the `onDeleted()` method, we "emit" a `Hero` object.
When something invokes the `onDeleted()` method, we "emit" a `Hero` object.
Now imagine a parent component that listens for that event with an Event Binding.
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-to-component')(format=".")
@ -588,7 +584,7 @@ code-example(format="", language="html").
Evaluation of an Event Binding template expression may have side-effects as this one clearly does.
They are not just OK (unlike in property bindings); they are expected.
The expression could update the model thereby triggering other changes that percolate through the system, changes that
are ultimately displayed in this view and other views.
The event processing may result in queries and saves to a remote server. It's all good.
@ -609,7 +605,7 @@ code-example(format="", language="html").
continues or stops with the current element.
Event propagation stops if the binding expression returns a falsey value (as does a method with no return value).
Clicking the button in this next example triggers a save;
Clicking the button in this next example triggers a save;
the click doesn't make it to the outer `<div>` so it's save is not called:
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-no-propagation')(format=".")
:marked
@ -645,7 +641,7 @@ code-example(format="", language="html").
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-3')(format=".")
:marked
Thats an improvement. It should be better.
We shouldn't have to mention the data property twice. Angular should be able to read the components data property and set it
with a single declaration &mdash; which it can with the `[{ }]` syntax:
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".")
@ -659,11 +655,11 @@ code-example(format="", language="html").
We can write our own two-way binding directive that follows this pattern if we're ever in the mood to do so.
:marked
Is `[{ngModel}]` all we need? Is there ever a reason to fall back to its expanded form?
Well `NgModel` can only set the target property.
Is `[(ngModel)]` all we need? Is there ever a reason to fall back to its expanded form?
Well `NgModel` can only set the target property.
What if we need to do something more or something different when the user changes the value?
Let's try something silly like forcing the input value to uppercase.
+makeExample('template-syntax/ts/app/app.component.html', 'NgModel-4')(format=".")
:marked
@ -677,7 +673,7 @@ figure.image-display
<a name="directives"></a>
## Built-in Directives
Earlier versions of Angular included over seventy built-in directives.
Earlier versions of Angular included over seventy built-in directives.
The community contributed many more and individuals have created countless private directives for internal applications.
We dont need many of those directives in Angular 2.
@ -727,7 +723,7 @@ figure.image-display
The `NgStyle` directive may be the better choice
when we want to set *many* inline styles at the same time.
We apply `NgStyle` by binding it to a key:value control object.
We apply `NgStyle` by binding it to a key:value control object.
Each key of the object is a style name and its value is whatever is appropriate for that style.
Consider a component method such as `setStyles` that returns an object defining three styles:
@ -761,25 +757,25 @@ figure.image-display
:marked
Hiding a sub-tree is quite different from excluding a sub-tree with `NgIf`.
When we hide the element sub-tree, it remains in the DOM.
Components in the sub-tree are preserved along with their state.
Angular may continue to check for changes even to invisible properties.
When we hide the element sub-tree, it remains in the DOM.
Components in the sub-tree are preserved along with their state.
Angular may continue to check for changes even to invisible properties.
The sub-tree may tie up substantial memory and computing resources.
When `NgIf` is `false`, Angular physically removes the element sub-tree from the DOM.
It destroys components in the sub-tree along with their state which may free up substantial resources
When `NgIf` is `false`, Angular physically removes the element sub-tree from the DOM.
It destroys components in the sub-tree along with their state which may free up substantial resources
resulting in better performance for the user.
The show/hide technique is probably fine for small element trees.
The show/hide technique is probably fine for small element trees.
We should be wary when hiding large trees; `NgIf` may be the safer choice. Always measure before leaping to conclusions.
<a id="ngSwitch"></a>
.l-main-section
:marked
### NgSwitch
We bind to `NgSwitch` when we want to display *one* element tree (an element and its children)
from a *set* of possible elment trees based on some condition.
Angular only puts the *selected* element tree into the DOM.
Angular only puts the *selected* element tree into the DOM.
Heres an example:
+makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch')(format=".")
@ -802,10 +798,10 @@ figure.image-display
:marked
### NgFor
`NgFor` is a “repeater” directive. It will be familiar if weve written repeaters for other view engines.
Our goal is to present a list of items. We define a block of HTML that defines how a single item should be displayed.
We tell Angular to use that block as a template for rendering each item in the list.
Here is an example of `NgFor` applied to a simple `<div>`.
+makeExample('template-syntax/ts/app/app.component.html', 'NgFor-1')(format=".")
:marked
@ -814,7 +810,7 @@ figure.image-display
.alert.is-critical
:marked
The leading asterisk (\*) in front of `ngFor` is a critical part of this syntax.
The leading asterisk (\*) in front of `ngFor` is a critical part of this syntax.
See the section below on [\* and &lt;template>](#star-template).
:marked
The text assigned to `*ngFor` is the instruction that guides the repeater process.
@ -822,26 +818,26 @@ figure.image-display
.l-sub-section
:marked
#### NgFor Micro-syntax
The string assigned to `*ngFor` is not a [template expression](#template-expressions).
The string assigned to `*ngFor` is not a [template expression](#template-expressions).
Its a little language of its own called a “micro-syntax” that Angular interprets. In this example it means:
>*Take each hero in the `heroes` array, store it in the local `hero` variable, and make it available to the templated HTML
for each iteration*.
Angular translates this instruction into a new set of elements and bindings.
Well talk about this in the next section about templates.
:marked
In our two examples, the `ngFor` directive iterates over the `heroes` array returned by the parent components `heroes` property
In our two examples, the `ngFor` directive iterates over the `heroes` array returned by the parent components `heroes` property
and stamps out instances of the element to which it is applied.
Angular creates a fresh instance of the template for each hero in the array.
The (#) character before "hero" identifies a [local template variable](#local-vars) called `hero`.
We reference this variable within the template to access a heros properties as were doing in the interpolation
or we can pass it in a binding to a component element as we're doing with `hero-detail`.
or we can pass it in a binding to a component element as we're doing with `hero-detail`.
#### NgFor with index
The `ngFor` directive supports an optional index that increases from 0 to the length of the array for each iteration.
The `ngFor` directive supports an optional index that increases from 0 to the length of the array for each iteration.
We can capture that in another local template variable (`i`) and use it in our template too.
This next example stamps out rows that display like "1 - Hercules Son of Zeus":
@ -868,9 +864,9 @@ figure.image-display
the *template-to-repeat* and the *template-to-include* respectively.
The (\*) prefix syntax is a convenient way for developers to skip the `<template>` wrapper tags and
focus directly on the HTML element to repeat or include.
focus directly on the HTML element to repeat or include.
Angular sees the (*) and expands the HTML into the `<template>` tags for us.
### Expanding `*ngIf`
We can do that ourselves if we wish. Instead of writing ...
+makeExample('template-syntax/ts/app/app.component.html', 'Template-1')(format=".")
@ -914,10 +910,10 @@ figure.image-display
.l-main-section
:marked
## Local template variables
A **local template variable** is a vehicle for moving data across element lines.
We've seen the `#hero` local template variable several times in this chapter,
We've seen the `#hero` local template variable several times in this chapter,
most prominently when writing [NgFor](#ngFor) repeaters.
In the [* and &lt;templates>](#star-template) segment we learned how Angular expands
@ -927,17 +923,17 @@ figure.image-display
The (#) prefix character in front of "hero" means that we're defining a `hero` variable.
.l-sub-section
:marked
Some folks don't like the (#) character.
Some folks don't like the (#) character.
The `var-` prefix is the “cannonical” alternative to "#". We could have declared our variable as `var-hero`.
:marked
We defined `hero` on the outer `<template>` element where it becomes the current hero item
We defined `hero` on the outer `<template>` element where it becomes the current hero item
as Angular iterates through the list of heroes.
The `hero` variable appears again in the binding on the inner `<hero-detail>` component element.
That's how each instance of the `<hero-detail>` gets its hero.
### Referencing a local template variable
We can reference a local template variable on the same element, on a sibling element, or on
any of its child elements.
@ -956,10 +952,10 @@ figure.image-display
We defined these variables on the `input` elements.
Were passing those `input` element objects across to the
button elements where they become arguments to the `call()` methods in the event bindings.
### NgForm and local template variables
Let's look at one final example, a Form, the poster child for local template variables.
The HTML for a form can be quite involved as we saw in the [Forms](forms.html) chapter.
The following is a *simplified* example &mdash; and it's not simple at all.
+makeExample('template-syntax/ts/app/app.component.html', 'var-form')
@ -969,14 +965,14 @@ figure.image-display
+makeExample('template-syntax/ts/app/app.component.html', 'var-form-a')
:marked
What is the value of `theForm`?
It would be the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)
It would be the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)
if Angular hadn't taken it over.
It's actually `ngForm`, a reference to the Angular built-in `NgForm` directive that wraps the native `HTMLFormElement`
and endows it with additional super powers such as the ability to
track the validity of user input.
This explains how we can disable the submit button by checking `theForm.form.valid`
This explains how we can disable the submit button by checking `theForm.form.valid`
and pass an object with rich information to the parent component's `onSubmit` method.
<a id="inputs-outputs"></a>
@ -984,25 +980,25 @@ figure.image-display
:marked
## Input and Output Properties
We can only bind to **target directive** properties that are either **inputs** or **outputs**.
.l-sub-section
:marked
We're drawing a sharp distinction between a data binding **target** and a data binding **source**.
The target is to the *left* of the (=) in a binding expression.
The source is on the *right* of the (=).
Every member of a **source** directive (typically a component) is automatically available for binding.
We don't have to do anything special to access a component member in the quoted template expression
to the right of the (=).
We have *limited* access to members of a **target** directive (typically a component).
We can only bind to *input* and *output* properties of target components to the left of the (=).
Also remember that a *component is a directive*.
Also remember that a *component is a directive*.
In this section, we use the terms "directive" and "component" interchangeably.
:marked
In this chapter weve focused mainly on binding to component members within template expressions
In this chapter weve focused mainly on binding to component members within template expressions
on the *right side of the binding declaration*.
A member in that position is a binding “data source”. It's not a target for binding.
@ -1040,17 +1036,17 @@ figure.image-display
Don't do both!
:marked
### Aliasing input/output properties
Sometimes we want the public name of the property to be different from the internal name.
This is frequently the case with [Attribute Directives](attribute-directives.html).
Directive consumers expect to bind to the name of the directive.
For example, we expect to bind to the `myClick` event property of the `MyClickDirective`.
For example, we expect to bind to the `myClick` event property of the `MyClickDirective`.
The directive name is often a poor choice for the the internal property name
because it rarely describes what the property does.
The corresponding `MyClickDirective` internal property is called `clicks`.
Fortunately, we can alias the internal name to meet the conventional needs of the directive's consumer.
We alias in decorator syntax like this:
+makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-1')(format=".")
@ -1059,7 +1055,7 @@ figure.image-display
The equivalent aliasing with the `outputs` array requires a colon-delimited string with
the internal property name on the left and the public name on the right:
+makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-2')(format=".")
<a id="expression-operators"></a>
.l-main-section
:marked
@ -1102,7 +1098,7 @@ figure.image-display
The view still renders but the displayed value is blank; we see only "`The title is`" with nothing after it.
That is reasonable behavior. At least the app doesn't crash.
Suppose the template expression involves a property path as in this next example
Suppose the template expression involves a property path as in this next example
where were displaying the `firstName` of a null hero.
code-example(format="" language="html").
@ -1121,7 +1117,7 @@ code-example(format="" language="html").
On the other hand, null values in the property path may be OK from time to time,
especially when we know the data will arrive eventually.
While we wait for data, the view should render without complaint and
the null property path should display as blank just as the `title` property does.
@ -1138,7 +1134,7 @@ code-example(format="" language="html").
Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`.
The Angular **“Elvis” operator ( ?. )** is a more fluent and convenient way to guard against nulls in property paths.
The expression bails out when it hits the first null value.
The expression bails out when it hits the first null value.
The display is blank but the app keeps rolling and there are no errors.
+makeExample('template-syntax/ts/app/app.component.html', 'elvis-6')(format=".")
:marked