parent
a89e57f1b1
commit
933433ceea
|
@ -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;
|
||||
we’ll discuss that option below.
|
||||
|
||||
|
@ -192,7 +192,7 @@ table
|
|||
But it’s also significantly different than the HTML we’re 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 element’s `innerHTML` property.
|
||||
Fortunately, the Angular template parser recognizes `inner-html` as an acceptable alias for `innerHTML`.
|
||||
:marked
|
||||
Let’s 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 element’s `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
|
||||
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.
|
||||
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’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.
|
||||
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
|
||||
That’s an improvement. It should be better.
|
||||
|
||||
|
||||
We shouldn't have to mention the data property twice. Angular should be able to read the component’s data property and set it
|
||||
with a single declaration — 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 don’t 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.
|
||||
|
||||
Here’s 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 we’ve 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 <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).
|
||||
It’s 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.
|
||||
We’ll 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 component’s `heroes` property
|
||||
In our two examples, the `ngFor` directive iterates over the `heroes` array returned by the parent component’s `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 hero’s properties as we’re 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 <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.
|
||||
We’re 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 — 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 we’ve focused mainly on binding to component members within template expressions
|
||||
In this chapter we’ve 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 we’re 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
|
||||
|
|
Loading…
Reference in New Issue