diff --git a/.pullapprove.yml b/.pullapprove.yml index 87b228c70d..9e6fa7ff9d 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -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', diff --git a/aio/content/examples/property-binding/src/app/app.component.ts b/aio/content/examples/property-binding/src/app/app.component.ts index cfe9abe4dd..5b58a8c6b3 100644 --- a/aio/content/examples/property-binding/src/app/app.component.ts +++ b/aio/content/examples/property-binding/src/app/app.component.ts @@ -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 diff --git a/aio/content/guide/event-binding.md b/aio/content/guide/event-binding.md index 40af4f6055..dc17ec9b46 100644 --- a/aio/content/guide/event-binding.md +++ b/aio/content/guide/event-binding.md @@ -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. diff --git a/aio/content/guide/property-binding-best-practices.md b/aio/content/guide/property-binding-best-practices.md new file mode 100644 index 0000000000..8713d5d77b --- /dev/null +++ b/aio/content/guide/property-binding-best-practices.md @@ -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. + +
+ +See the for a working example containing the code snippets in this guide. + +
+ +## 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. + + + +You can confirm this expectation by looking in the `ItemDetailComponent` where the `@Input()` type is `string`: + + + +The `parentItem` in `AppComponent` is a string, which means that the expression, `parentItem` within `[childItem]="parentItem"`, evaluates to a string. + + + +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. + + + +In the `ItemListComponent` the `@Input()`, `items`, has a type of `Item[]`. + + + +Notice that `Item` is an object that it has two properties; an `id` and a `name`. + + + +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`. + + + +By supplying an object in the same shape, you satisfy the expectations of `items` when Angular evaluates the expression `currentItems`. diff --git a/aio/content/guide/property-binding.md b/aio/content/guide/property-binding.md index ed2f877e17..c9240da7ae 100644 --- a/aio/content/guide/property-binding.md +++ b/aio/content/guide/property-binding.md @@ -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.
@@ -10,34 +10,57 @@ See the for a working example containing the code
-## 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). +
-## 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. + +
+ +For more information on listening for events, see [Event binding](guide/event-binding). + +
+ +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. -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`. - +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 `` 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. + + + +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 `` 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`. + + + +Declare the `itemImageUrl` property in the class, in this case `AppComponent`. + + {@a colspan} @@ -81,175 +104,100 @@ for parent and child components to communicate: -## 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 - +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: + - +Because the value of the property `isUnchanged` is `true` in the `AppComponent`, Angular disables the button. + + -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 `` 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`. -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. + -
+Angular applies the class `special` to the `

` 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 -

- - -## 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`. -You can confirm this by looking in the `ItemDetailComponent` where the `@Input` type is set to a string: - +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. + + + +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: -### Passing in an object +With this configuration, the view of `` 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. - - - -The `items` property is declared in the `ItemListComponent` with a type of `Item` and decorated with `@Input()`: - - - -In this sample app, an `Item` is an object that has two properties; an `id` and a `name`. - - - -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: - - - -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: - - - - -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. - - - -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: - - - -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. -In the component template, the content might be used with interpolation: +The component template interpolates the content as follows: -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. - - - -Interpolation handles the `