docs: edit property binding doc (#38799)
This commit edits the property binding doc copy and adds some docregions to clarify explanations. PR Close #38799
This commit is contained in:
parent
cfac8e0764
commit
3e6f24edcd
@ -380,6 +380,7 @@ groups:
|
||||
'aio/content/examples/binding-syntax/**',
|
||||
'aio/content/guide/property-binding.md',
|
||||
'aio/content/examples/property-binding/**',
|
||||
'aio/content/guide/property-binding-best-practices.md',
|
||||
'aio/content/guide/attribute-binding.md',
|
||||
'aio/content/examples/attribute-binding/**',
|
||||
'aio/content/guide/two-way-binding.md',
|
||||
|
@ -7,9 +7,15 @@ import { Component } from '@angular/core';
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
// #docregion item-image
|
||||
itemImageUrl = '../assets/phone.png';
|
||||
// #enddocregion item-image
|
||||
// #docregion boolean
|
||||
isUnchanged = true;
|
||||
// #enddocregion boolean
|
||||
// #docregion directive-property
|
||||
classes = 'special';
|
||||
// #enddocregion directive-property
|
||||
// #docregion parent-data-type
|
||||
parentItem = 'lamp';
|
||||
// #enddocregion parent-data-type
|
||||
|
@ -99,7 +99,7 @@ in the `$event` variable.
|
||||
|
||||
## Template statements have side effects
|
||||
|
||||
Though [template expressions](guide/interpolation#template-expressions) shouldn't have [side effects](guide/property-binding#avoid-side-effects), template
|
||||
Though [template expressions](guide/interpolation#template-expressions) shouldn't have [side effects](guide/property-binding-best-practices#avoid-side-effects), template
|
||||
statements usually do. The `deleteItem()` method does have
|
||||
a side effect: it deletes an item.
|
||||
|
||||
|
65
aio/content/guide/property-binding-best-practices.md
Normal file
65
aio/content/guide/property-binding-best-practices.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Property binding best practices
|
||||
|
||||
By following a few guidelines, you can use property binding in a way that helps you minimize bugs and keep your code readable.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
See the <live-example name="property-binding"></live-example> for a working example containing the code snippets in this guide.
|
||||
|
||||
</div>
|
||||
|
||||
## Avoid side effects
|
||||
|
||||
Evaluation of a template expression should have no visible side effects.
|
||||
Use the syntax for template expressions to help avoid side effects.
|
||||
In general, the correct syntax prevents you from assigning a value to anything in a property binding expression.
|
||||
The syntax also prevents you from using increment and decrement operators.
|
||||
|
||||
### An example of producing side effects
|
||||
|
||||
If you had an expression that changed the value of something else that you were binding to, that change of value would be a side effect.
|
||||
Angular might or might not display the changed value.
|
||||
If Angular does detect the change, it throws an error.
|
||||
|
||||
As a best practice, use only properties and methods that return values.
|
||||
|
||||
## Return the proper type
|
||||
|
||||
A template expression should evaluate to the type of value that the target property expects.
|
||||
For example, return a string if the target property expects a string, a number if it expects a number, or an object if it expects an object.
|
||||
|
||||
### Passing in a string
|
||||
|
||||
In the following example, the `childItem` property of the `ItemDetailComponent` expects a string.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can confirm this expectation by looking in the `ItemDetailComponent` where the `@Input()` type is `string`:
|
||||
|
||||
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts (setting the @Input() type)"></code-example>
|
||||
|
||||
The `parentItem` in `AppComponent` is a string, which means that the expression, `parentItem` within `[childItem]="parentItem"`, evaluates to a string.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="parent-data-type" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
If `parentItem` were some other type, you would need to specify `childItem` `@Input()` as that type as well.
|
||||
|
||||
### Passing in an object
|
||||
|
||||
In this example, `ItemListComponent` is a child component of `AppComponent` and the `items` property expects an array of objects.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html"></code-example>
|
||||
|
||||
In the `ItemListComponent` the `@Input()`, `items`, has a type of `Item[]`.
|
||||
|
||||
<code-example path="property-binding/src/app/item-list/item-list.component.ts" region="item-input" header="src/app/item-list.component.ts"></code-example>
|
||||
|
||||
Notice that `Item` is an object that it has two properties; an `id` and a `name`.
|
||||
|
||||
<code-example path="property-binding/src/app/item.ts" region="item-class" header="src/app/item.ts"></code-example>
|
||||
|
||||
In `app.component.ts`, `currentItems` is an array of objects in the same shape as the `Item` object in `items.ts`, with an `id` and a `name`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="pass-object" header="src/app.component.ts"></code-example>
|
||||
|
||||
By supplying an object in the same shape, you satisfy the expectations of `items` when Angular evaluates the expression `currentItems`.
|
@ -1,8 +1,8 @@
|
||||
|
||||
# Property binding `[property]`
|
||||
# Property binding
|
||||
|
||||
Use property binding to _set_ properties of target elements or
|
||||
directive `@Input()` decorators.
|
||||
Property binding in Angular helps you set values for properties of HTML elements or directives.
|
||||
With property binding, you can do things such as toggle button functionality, set paths programatically, and share values between components.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -10,34 +10,57 @@ See the <live-example></live-example> for a working example containing the code
|
||||
|
||||
</div>
|
||||
|
||||
## One-way in
|
||||
## Prerequisites
|
||||
|
||||
Property binding flows a value in one direction,
|
||||
from a component's property into a target element property.
|
||||
To get the most out of property binding, you should be familiar with the following:
|
||||
|
||||
You can't use property
|
||||
binding to read or pull values out of target elements. Similarly, you cannot use
|
||||
property binding to call a method on the target element.
|
||||
If the element raises events, you can listen to them with an [event binding](guide/event-binding).
|
||||
* [Basics of components](guide/architecture-components)
|
||||
* [Basics of templates](guide/glossary#template)
|
||||
* [Binding syntax](guide/binding-syntax)
|
||||
|
||||
If you must read a target element property or call one of its methods,
|
||||
see the API reference for [ViewChild](api/core/ViewChild) and
|
||||
[ContentChild](api/core/ContentChild).
|
||||
<hr />
|
||||
|
||||
## Examples
|
||||
## Understanding the flow of data
|
||||
|
||||
The most common property binding sets an element property to a component
|
||||
property value. An example is
|
||||
binding the `src` property of an image element to a component's `itemImageUrl` property:
|
||||
Property binding moves a value in one direction, from a component's property into a target element property.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information on listening for events, see [Event binding](guide/event-binding).
|
||||
|
||||
</div>
|
||||
|
||||
To read a target element property or call one of its methods, see the API reference for [ViewChild](api/core/ViewChild) and [ContentChild](api/core/ContentChild).
|
||||
|
||||
## Binding to a property
|
||||
|
||||
To bind to an element's property, enclose it in square brackets, `[]`, which identifies the property as a target property.
|
||||
A target property is the DOM property to which you want to assign a value.
|
||||
For example, the target property in the following code is the image element's `src` property.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Here's an example of binding to the `colSpan` property. Notice that it's not `colspan`,
|
||||
which is the attribute, spelled with a lowercase `s`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
||||
In most cases, the target name is the name of a property, even when it appears to be the name of an attribute.
|
||||
In this example, `src` is the name of the `<img>` element property.
|
||||
|
||||
For more details, see the [MDN HTMLTableCellElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
|
||||
The brackets, `[]`, cause Angular to evaluate the right-hand side of the assignment as a dynamic expression.
|
||||
Without the brackets, Angular treats the the right-hand side as a string literal and sets the property to that static value.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="no-evaluation" header="src/app.component.html"></code-example>
|
||||
|
||||
Omitting the brackets renders the string `parentItem`, not the value of `parentItem`.
|
||||
|
||||
## Setting an element property to a component property value
|
||||
|
||||
To bind the `src` property of an `<img>` element to a component's property, place the target, `src`, in square brackets followed by an equal sign and then the property.
|
||||
The property here is `itemImageUrl`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Declare the `itemImageUrl` property in the class, in this case `AppComponent`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="item-image" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
{@a colspan}
|
||||
|
||||
@ -81,175 +104,100 @@ for parent and child components to communicate:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
## Binding targets
|
||||
|
||||
An element property between enclosing square brackets identifies the target property.
|
||||
The target property in the following code is the image element's `src` property.
|
||||
## Toggling button functionality
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
|
||||
To disable a button's functionality depending on a Boolean value, bind the DOM `disabled` property to a property in the class that is `true` or `false`.
|
||||
|
||||
There's also the `bind-` prefix alternative:
|
||||
<code-example path="property-binding/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="bind-prefix" header="src/app/app.component.html"></code-example>
|
||||
Because the value of the property `isUnchanged` is `true` in the `AppComponent`, Angular disables the button.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="boolean" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
|
||||
In most cases, the target name is the name of a property, even
|
||||
when it appears to be the name of an attribute.
|
||||
So in this case, `src` is the name of the `<img>` element property.
|
||||
## Setting a directive property
|
||||
|
||||
Element properties may be the more common targets,
|
||||
but Angular looks first to see if the name is a property of a known directive,
|
||||
as it is in the following example:
|
||||
To set a property of a directive, place the directive within square brackets , such as `[ngClass]`, followed by an equal sign and the property.
|
||||
Here, the property is `classes`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Technically, Angular is matching the name to a directive `@Input()`,
|
||||
one of the property names listed in the directive's `inputs` array
|
||||
or a property decorated with `@Input()`.
|
||||
Such inputs map to the directive's own properties.
|
||||
To use the property, you must declare it in the class, which in this example is `AppComponent`.
|
||||
The value of `classes` is `special`.
|
||||
|
||||
If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error.
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="directive-property" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
Angular applies the class `special` to the `<p>` element so that you can use `special` to apply CSS styles.
|
||||
|
||||
Though the target name is usually the name of a property,
|
||||
there is an automatic attribute-to-property mapping in Angular for
|
||||
several common attributes. These include `class`/`className`, `innerHtml`/`innerHTML`, and
|
||||
`tabindex`/`tabIndex`.
|
||||
## Bind values between components
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Avoid side effects
|
||||
|
||||
Evaluation of a template expression should have no visible side effects.
|
||||
The expression language itself, or the way you write template expressions,
|
||||
helps to a certain extent;
|
||||
you can't assign a value to anything in a property binding expression
|
||||
nor use the increment and decrement operators.
|
||||
|
||||
For example, you could have an expression that invoked a property or method that had
|
||||
side effects. The expression could call something like `getFoo()` where only you
|
||||
know what `getFoo()` does. If `getFoo()` changes something
|
||||
and you happen to be binding to that something,
|
||||
Angular may or may not display the changed value. Angular may detect the
|
||||
change and throw a warning error.
|
||||
As a best practice, stick to properties and to methods that return
|
||||
values and avoid side effects.
|
||||
|
||||
## Return the proper type
|
||||
|
||||
The template expression should evaluate to the type of value
|
||||
that the target property expects.
|
||||
Return a string if the target property expects a string, a number if it
|
||||
expects a number, an object if it expects an object, and so on.
|
||||
|
||||
In the following example, the `childItem` property of the `ItemDetailComponent` expects a string, which is exactly what you're sending in the property binding:
|
||||
To set the model property of a custom component, place the target, here `childItem`, between square brackets `[]` followed by an equal sign and the property.
|
||||
Here, the property is `parentItem`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can confirm this by looking in the `ItemDetailComponent` where the `@Input` type is set to a string:
|
||||
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts (setting the @Input() type)"></code-example>
|
||||
To use the target and the property, you must declare them in their respective classes.
|
||||
|
||||
Declare the target of `childItem` in its component class, in this case `ItemDetailComponent`.
|
||||
|
||||
For example, the following code declares the target of `childItem` in its component class, in this case `ItemDetailComponent`.
|
||||
|
||||
Then, the code contains an `@Input()` decorator with the `childItem` property so data can flow into it.
|
||||
|
||||
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts"></code-example>
|
||||
|
||||
Next, the code declares the property of `parentItem` in its component class, in this case `AppComponent`.
|
||||
In this example the type of `childItem` is `string`, so `parentItem` needs to be a string.
|
||||
Here, `parentItem` has the string value of `lamp`.
|
||||
|
||||
As you can see here, the `parentItem` in `AppComponent` is a string, which the `ItemDetailComponent` expects:
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="parent-data-type" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
### Passing in an object
|
||||
With this configuration, the view of `<app-item-detail>` uses the value of `lamp` for `childItem`.
|
||||
|
||||
The previous simple example showed passing in a string. To pass in an object,
|
||||
the syntax and thinking are the same.
|
||||
## Property binding and security
|
||||
|
||||
In this scenario, `ItemListComponent` is nested within `AppComponent` and the `items` property expects an array of objects.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The `items` property is declared in the `ItemListComponent` with a type of `Item` and decorated with `@Input()`:
|
||||
|
||||
<code-example path="property-binding/src/app/item-list/item-list.component.ts" region="item-input" header="src/app/item-list.component.ts"></code-example>
|
||||
|
||||
In this sample app, an `Item` is an object that has two properties; an `id` and a `name`.
|
||||
|
||||
<code-example path="property-binding/src/app/item.ts" region="item-class" header="src/app/item.ts"></code-example>
|
||||
|
||||
While a list of items exists in another file, `mock-items.ts`, you can
|
||||
specify a different item in `app.component.ts` so that the new item will render:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="pass-object" header="src/app.component.ts"></code-example>
|
||||
|
||||
You just have to make sure, in this case, that you're supplying an array of objects because that's the type of `Item` and is what the nested component, `ItemListComponent`, expects.
|
||||
|
||||
In this example, `AppComponent` specifies a different `item` object
|
||||
(`currentItems`) and passes it to the nested `ItemListComponent`. `ItemListComponent` was able to use `currentItems` because it matches what an `Item` object is according to `item.ts`. The `item.ts` file is where
|
||||
`ItemListComponent` gets its definition of an `item`.
|
||||
|
||||
## Remember the brackets
|
||||
|
||||
The brackets, `[]`, tell Angular to evaluate the template expression.
|
||||
If you omit the brackets, Angular treats the string as a constant
|
||||
and *initializes the target property* with that string:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="no-evaluation" header="src/app.component.html"></code-example>
|
||||
|
||||
|
||||
Omitting the brackets will render the string
|
||||
`parentItem`, not the value of `parentItem`.
|
||||
|
||||
## One-time string initialization
|
||||
|
||||
You *should* omit the brackets when all of the following are true:
|
||||
|
||||
* The target property accepts a string value.
|
||||
* The string is a fixed value that you can put directly into the template.
|
||||
* This initial value never changes.
|
||||
|
||||
You routinely initialize attributes this way in standard HTML, and it works
|
||||
just as well for directive and component property initialization.
|
||||
The following example initializes the `prefix` property of the `StringInitComponent` to a fixed string,
|
||||
not a template expression. Angular sets it and forgets about it.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="string-init" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The `[item]` binding, on the other hand, remains a live binding to the component's `currentItems` property.
|
||||
|
||||
## Property binding vs. interpolation
|
||||
|
||||
You often have a choice between interpolation and property binding.
|
||||
The following binding pairs do the same thing:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Interpolation is a convenient alternative to property binding in
|
||||
many cases. When rendering data values as strings, there is no
|
||||
technical reason to prefer one form to the other, though readability
|
||||
tends to favor interpolation. However, *when setting an element
|
||||
property to a non-string data value, you must use property binding*.
|
||||
|
||||
## Content security
|
||||
|
||||
Imagine the following malicious content.
|
||||
Property binding can help keep content secure.
|
||||
For example, consider the following malicious content.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="malicious-content" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
In the component template, the content might be used with interpolation:
|
||||
The component template interpolates the content as follows:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Fortunately, Angular data binding is on alert for dangerous HTML. In the above case,
|
||||
the HTML displays as is, and the Javascript does not execute. Angular **does not**
|
||||
allow HTML with script tags to leak into the browser, neither with interpolation
|
||||
nor property binding.
|
||||
|
||||
In the following example, however, Angular [sanitizes](guide/security#sanitization-and-security-contexts)
|
||||
the values before displaying them.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-content" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Interpolation handles the `<script>` tags differently than
|
||||
property binding but both approaches render the
|
||||
content harmlessly. The following is the browser output
|
||||
of the `evilTitle` examples.
|
||||
The browser doesn't process the HTML and instead displays it raw, as follows.
|
||||
|
||||
<code-example language="bash">
|
||||
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
|
||||
</code-example>
|
||||
|
||||
|
||||
Angular does not allow HTML with `<script>` tags, neither with [interpolation](guide/interpolation) nor property binding, which prevents the JavaScript from running.
|
||||
|
||||
In the following example, however, Angular [sanitizes](guide/security#sanitization-and-security-contexts) the values before displaying them.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-content" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Interpolation handles the `<script>` tags differently than property binding, but both approaches render the content harmlessly.
|
||||
The following is the browser output of the sanitized `evilTitle` example.
|
||||
|
||||
<code-example language="bash">
|
||||
"Template Syntax" is the property bound evil title.
|
||||
</code-example>
|
||||
|
||||
## Property binding and interpolation
|
||||
|
||||
Often [interpolation](guide/interpolation) and property binding can achieve the same results.
|
||||
The following binding pairs do the same thing.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can use either form when rendering data values as strings, though interpolation is preferable for readability.
|
||||
However, when setting an element property to a non-string data value, you must use property binding.
|
||||
|
||||
<hr />
|
||||
|
||||
## What's next
|
||||
|
||||
* [Property binding best practices](guide/property-binding-best-practices)
|
||||
|
@ -422,6 +422,11 @@
|
||||
"title": "Keeping Up-to-Date",
|
||||
"tooltip": "Information about updating Angular applications and libraries to the latest version."
|
||||
},
|
||||
{
|
||||
"url": "guide/property-binding-best-practices",
|
||||
"title": "Property Binding Best Practices",
|
||||
"tooltip": "Use property binding efficiently."
|
||||
},
|
||||
{
|
||||
"title": "Testing",
|
||||
"tooltip": "Testing your Angular apps.",
|
||||
|
Loading…
x
Reference in New Issue
Block a user