docs: rewrite property binding section and add example (#25770)

PR Close #25770
This commit is contained in:
Kapunahele Wong 2018-08-28 12:46:59 -04:00 committed by Andrew Kushnir
parent 4ad323a4d6
commit 85d38ae564
26 changed files with 601 additions and 101 deletions

View File

@ -0,0 +1,54 @@
import { browser, element, by } from 'protractor';
describe('Property binding e2e tests', () => {
beforeEach(function () {
browser.get('');
});
it('should display Property Binding with Angular', function () {
expect(element(by.css('h1')).getText()).toEqual('Property Binding with Angular');
});
it('should display four phone pictures', function() {
expect(element.all(by.css('img')).isPresent()).toBe(true);
expect(element.all(by.css('img')).count()).toBe(4);
});
it('should display Disabled button', function () {
expect(element.all(by.css('button')).get(0).getText()).toBe(`Disabled Button`);
});
it('should display Binding to a property of a directive', function () {
expect(element.all(by.css('h2')).get(4).getText()).toBe(`Binding to a property of a directive`);
});
it('should display Your item is: lamp', function () {
expect(element.all(by.css('p')).get(0).getText()).toContain(`blue`);
});
it('should display Your item is: lamp', function () {
expect(element.all(by.css('p')).get(1).getText()).toContain(`Your item is: lamp`);
});
it('should display Your item is: parentItem', function () {
expect(element.all(by.css('p')).get(2).getText()).toBe(`Your item is: parentItem`);
});
it('should display a ul', function () {
expect(element.all(by.css('ul')).get(0).getText()).toContain(`tv`);
});
it('should display a ul containing phone', function () {
expect(element.all(by.css('ul')).get(1).getText()).toBe(`21 phone`);
});
it('should display one-time initialized string', function () {
expect(element.all(by.css('p')).get(3).getText()).toContain(`one-time initialized`);
});
it('should display Malicious content', function () {
expect(element.all(by.css('h2')).get(8).getText()).toBe(`Malicious content`);
});
});

View File

@ -0,0 +1,9 @@
div {
margin: 1rem auto;
width: 90%
}
.special {
background-color: #1976d2;
color: #fff;
padding: 1rem;
}

View File

@ -0,0 +1,84 @@
<div>
<h1>Property Binding with Angular</h1>
<h2>Binding the src property of an image:</h2>
<!-- #docregion property-binding -->
<img [src]="itemImageUrl">
<!-- #enddocregion property-binding -->
<h2>Using bind- syntax:</h2>
<!-- #docregion bind-prefix -->
<img bind-src="itemImageUrl">
<!-- #enddocregion bind-prefix -->
<hr />
<h2>Binding to the colSpan property</h2>
<table border=1>
<tr><td>Column 1</td><td>Column 2</td></tr>
<!-- #docregion colSpan -->
<!-- Notice the colSpan property is camel case -->
<tr><td [colSpan]="2">Span 2 columns</td></tr>
<!-- #enddocregion colSpan -->
</table>
<hr />
<h2>Button disabled state bound to isUnchanged property:</h2>
<!-- #docregion disabled-button -->
<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Disabled Button</button>
<!-- #enddocregion disabled-button -->
<hr />
<h2>Binding to a property of a directive</h2>
<!-- #docregion class-binding -->
<p [ngClass]="classes">[ngClass] binding to the classes property making this blue</p>
<!-- #enddocregion class-binding -->
<hr />
<h2>Model property of a custom component:</h2>
<!-- #docregion model-property-binding -->
<app-item-detail [childItem]="parentItem"></app-item-detail>
<!-- #enddocregion model-property-binding -->
<!-- #docregion no-evaluation -->
<app-item-detail childItem="parentItem"></app-item-detail>
<!-- #enddocregion no-evaluation -->
<h3>Pass objects:</h3>
<!-- #docregion pass-object -->
<app-list-item [items]="currentItem"></app-list-item>
<!-- #enddocregion pass-object -->
<hr />
<h2>Initialized string:</h2>
<!-- #docregion string-init -->
<app-string-init prefix="This is a one-time initialized string."></app-string-init>
<!-- #enddocregion string-init -->
<hr />
<h2>Property binding and interpolation</h2>
<!-- #docregion property-binding-interpolation -->
<p><img src="{{itemImageUrl}}"> is the <i>interpolated</i> image.</p>
<p><img [src]="itemImageUrl"> is the <i>property bound</i> image.</p>
<p><span>"{{interpolationTitle}}" is the <i>interpolated</i> title.</span></p>
<p>"<span [innerHTML]="propertyTitle"></span>" is the <i>property bound</i> title.</p>
<!-- #enddocregion property-binding-interpolation -->
<hr />
<h2>Malicious content</h2>
<!-- #docregion malicious-interpolated -->
<p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p>
<!-- #enddocregion malicious-interpolated -->
<!-- #docregion malicious-content -->
<!--
Angular generates a warning for the following line as it sanitizes them
WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).
-->
<p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>
<!-- #enddocregion malicious-content -->
</div>

View File

@ -0,0 +1,27 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});

View File

@ -0,0 +1,30 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
itemImageUrl = '../assets/phone.png';
isUnchanged = true;
classes = 'special';
// #docregion parent-data-type
parentItem = 'lamp';
// #enddocregion parent-data-type
// #docregion pass-object
currentItem = [{
id: 21,
name: 'phone'
}];
// #enddocregion pass-object
interpolationTitle = 'Interpolation';
propertyTitle = 'Property binding';
// #docregion malicious-content
evilTitle = 'Template <script>alert("evil never sleeps")</script> Syntax';
// #enddocregion malicious-content
}

View File

@ -0,0 +1,24 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ItemDetailComponent } from './item-detail/item-detail.component';
import { ListItemComponent } from './list-item/list-item.component';
import { StringInitComponent } from './string-init/string-init.component';
@NgModule({
declarations: [
AppComponent,
ItemDetailComponent,
ListItemComponent,
StringInitComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -0,0 +1,4 @@
<p>Your item is: {{ childItem }} </p>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemDetailComponent } from './item-detail.component';
describe('ItemDetailComponent', () => {
let component: ItemDetailComponent;
let fixture: ComponentFixture<ItemDetailComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ItemDetailComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,26 @@
import { Component, OnInit, Input } from '@angular/core';
// import { Item } from '../item';
// import { ITEMS } from '../mock-items';
@Component({
selector: 'app-item-detail',
templateUrl: './item-detail.component.html',
styleUrls: ['./item-detail.component.css']
})
export class ItemDetailComponent implements OnInit {
// #docregion input-type
@Input() childItem: string;
// #enddocregion input-type
// items = ITEMS;
currentItem = 'bananas in boxes';
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,7 @@
// #docregion item-class
export class Item {
id: number;
name: string;
}
// #enddocregion item-class

View File

@ -0,0 +1,11 @@
<h4>Nested component's list of items:</h4>
<ul>
<li *ngFor="let item of listItems">{{item.id}} {{item.name}}</li>
</ul>
<h4>Pass an object from parent to nested component:</h4>
<ul>
<li *ngFor="let item of items">{{item.id}} {{item.name}}</li>
</ul>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ListItemComponent } from './list-item.component';
describe('ItemListComponent', () => {
let component: ListItemComponent;
let fixture: ComponentFixture<ListItemComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ListItemComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ListItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,17 @@
import { Component, Input } from '@angular/core';
import { ITEMS } from '../mock-items';
import { Item } from '../item';
@Component({
selector: 'app-list-item',
templateUrl: './list-item.component.html',
styleUrls: ['./list-item.component.css']
})
export class ListItemComponent {
listItems = ITEMS;
// #docregion item-input
@Input() items: Item[];
// #enddocregion item-input
constructor() { }
}

View File

@ -0,0 +1,14 @@
import { Item } from './item';
export const ITEMS: Item[] = [
{ id: 11, name: 'bottle' },
{ id: 12, name: 'boombox' },
{ id: 13, name: 'chair' },
{ id: 14, name: 'fishbowl' },
{ id: 15, name: 'lamp' },
{ id: 16, name: 'tv' },
{ id: 17, name: 'mug' },
{ id: 18, name: 'paintbrush' },
{ id: 19, name: 'plant' },
{ id: 20, name: 'teapot' }
];

View File

@ -0,0 +1 @@
<p>{{prefix}}</p>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StringInitComponent } from './string-init.component';
describe('StringInitComponent', () => {
let component: StringInitComponent;
let fixture: ComponentFixture<StringInitComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ StringInitComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StringInitComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,17 @@
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-string-init',
templateUrl: './string-init.component.html',
styleUrls: ['./string-init.component.css']
})
export class StringInitComponent implements OnInit {
@Input() prefix: string;
constructor() { }
ngOnInit() {
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PropertyBinding</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));

View File

@ -0,0 +1,10 @@
{
"description": "Property Binding",
"files": [
"!**/*.d.ts",
"!**/*.js",
"!**/*.[0,1,2].*"
],
"file": "src/app/app.component.ts",
"tags": ["property binding"]
}

View File

@ -643,188 +643,252 @@ The following table summarizes:
{@a property-binding} {@a property-binding}
## Property binding ( <span class="syntax">[property]</span> ) ## Property binding `[property]`
Write a template **property binding** to set a property of a view element. Use property binding to _set_ properties of target elements or
The binding sets the property to the value of a [template expression](guide/template-syntax#template-expressions). directive `@Input()` decorators. For an example
demonstrating all of the points in this section, see the
<live-example name="property-binding">property binding example</live-example>.
The most common property binding sets an element property to a component property value. An example is ### One-way in
binding the `src` property of an image element to a component's `heroImageUrl` property:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-1" header="src/app/app.component.html" linenums="false"> Property binding flows a value in one direction,
from a component's property into a target element property.
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/template-syntax#event-binding).
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
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:
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html" linenums="false">
</code-example> </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" linenums="false">
</code-example>
For more details, see the [MDN HTMLTableCellElment](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
<!-- Add link when Attribute Binding updates are merged:
For more about `colSpan` and `colspan`, see (Attribute Binding)[guide/template-syntax]. -->
Another example is disabling a button when the component says that it `isUnchanged`: Another example is disabling a button when the component says that it `isUnchanged`:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-2" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
Another is setting a property of a directive: Another is setting a property of a directive:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-3" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
Yet another is setting the model property of a custom component (a great way Yet another is setting the model property of a custom component&mdash;a great way
for parent and child components to communicate): for parent and child components to communicate:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-4" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
### One-way *in*
People often describe property binding as *one-way data binding* because it flows a value in one direction,
from a component's data property into a target element property.
You cannot use property binding to pull values *out* of the target element.
You can't bind to a property of the target element to _read_ it. You can only _set_ it.
<div class="alert is-helpful">
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/template-syntax#event-binding).
If you must read a target element property or call one of its methods,
you'll need a different technique.
See the API reference for
[ViewChild](api/core/ViewChild) and
[ContentChild](api/core/ContentChild).
</div>
### Binding target ### Binding target
An element property between enclosing square brackets identifies the target property. 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. The target property in the following code is the image element's `src` property.
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-1" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
Some people prefer the `bind-` prefix alternative, known as the *canonical form*: There's also the `bind-` prefix alternative:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-5" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="bind-prefix" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
The target name is always the name of a property, even when it appears to be the name of something else.
You see `src` and may think it's the name of an attribute. No. It's the name of an image element property. 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.
Element properties may be the more common targets, Element properties may be the more common targets,
but Angular looks first to see if the name is a property of a known directive, but Angular looks first to see if the name is a property of a known directive,
as it is in the following example: as it is in the following example:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-3" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
<div class="alert is-helpful"> Technically, Angular is matching the name to a directive `@Input()`,
one of the property names listed in the directive's `inputs` array
Technically, Angular is matching the name to a directive [input](guide/template-syntax#inputs-outputs), or a property decorated with `@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. Such inputs map to the directive's own properties.
</div>
If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error. If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error.
<div class="alert is-helpful">
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`.
</div>
### Avoid side effects ### Avoid side effects
As mentioned previously, evaluation of a template expression should have no visible side effects. Evaluation of a template expression should have no visible side effects.
The expression language itself does its part to keep you safe. The expression language itself, or the way you write template expressions,
You can't assign a value to anything in a property binding expression nor use the increment and decrement operators. 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.
Of course, the expression might invoke a property or method that has side effects. For example, you could have an expression that invoked a property or method that had
Angular has no way of knowing that or stopping you. side effects. The expression could call something like `getFoo()` where only you
know what `getFoo()` does. If `getFoo()` changes something
The expression could call something like `getFoo()`. Only you know what `getFoo()` does. and you happen to be binding to that something,
If `getFoo()` changes something and you happen to be binding to that something, you risk an unpleasant experience. Angular may or may not display the changed value. Angular may detect the
Angular may or may not display the changed value. Angular may detect the change and throw a warning error. change and throw a warning error.
In general, stick to data properties and to methods that return values and do no more. As a best practice, stick to properties and to methods that return
values and avoid side effects.
### Return the proper type ### Return the proper type
The template expression should evaluate to the type of value expected by the target property. The template expression should evaluate to the type of value
Return a string if the target property expects a string. that the target property expects.
Return a number if the target property expects a number. Return a string if the target property expects a string, a number if it
Return an object if the target property expects an object. expects a number, an object if it expects an object, and so on.
The `hero` property of the `HeroDetail` component expects a `Hero` object, which is exactly what you're sending in the property binding: In the following example, the `childItem` property of the `ItemDetailComponent` expects a string, which is exactly what you're sending in the property binding:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-4" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html" linenums="false">
</code-example> </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" linenums="false">
</code-example>
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" linenums="false">
</code-example>
#### Passing in an object
The previous simple example showed passing in a string. To pass in an object,
the syntax and thinking are the same.
In this scenario, `ListItemComponent` is nested within `AppComponent` and the `item` property expects an object.
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html" linenums="false">
</code-example>
The `item` property is declared in the `ListItemComponent` with a type of `Item` and decorated with `@Input()`:
<code-example path="property-binding/src/app/list-item/list-item.component.ts" region="item-input" header="src/app/list-item.component.ts" linenums="false">
</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" linenums="false">
</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" linenums="false">
</code-example>
You just have to make sure, in this case, that you're supplying an object because that's the type of `item` and is what the nested component, `ListItemComponent`, expects.
In this example, `AppComponent` specifies a different `item` object
(`currentItem`) and passes it to the nested `ListItemComponent`. `ListItemComponent` was able to use `currentItem` because it matches what an `Item` object is according to `item.ts`. The `item.ts` file is where
`ListItemComponent` gets its definition of an `item`.
### Remember the brackets ### Remember the brackets
The brackets tell Angular to evaluate the template expression. The brackets, `[]`, tell Angular to evaluate the template expression.
If you omit the brackets, Angular treats the string as a constant If you omit the brackets, Angular treats the string as a constant
and *initializes the target property* with that string. and *initializes the target property* with that string:
It does *not* evaluate the string!
Don't make the following mistake: <code-example path="property-binding/src/app/app.component.html" region="no-evaluation" header="src/app.component.html" linenums="false">
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-6" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
{@a one-time-initialization}
Omitting the brackets will render the string
`parentItem`, not the value of `parentItem`.
### One-time string initialization ### One-time string initialization
You *should* omit the brackets when all of the following are true: You *should* omit the brackets when all of the following are true:
* The target property accepts a string value. * The target property accepts a string value.
* The string is a fixed value that you can bake into the template. * The string is a fixed value that you can put directly into the template.
* This initial value never changes. * This initial value never changes.
You routinely initialize attributes this way in standard HTML, and it works You routinely initialize attributes this way in standard HTML, and it works
just as well for directive and component property initialization. just as well for directive and component property initialization.
The following example initializes the `prefix` property of the `HeroDetailComponent` to a fixed string, 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. not a template expression. Angular sets it and forgets about it.
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-7" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="string-init" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
The `[hero]` binding, on the other hand, remains a live binding to the component's `currentHero` property. The `[item]` binding, on the other hand, remains a live binding to the component's `currentItem` property.
{@a property-binding-or-interpolation} ### Property binding vs. interpolation
### Property binding or interpolation?
You often have a choice between interpolation and property binding. You often have a choice between interpolation and property binding.
The following binding pairs do the same thing: The following binding pairs do the same thing:
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-vs-interpolation" header="src/app/app.component.html" linenums="false"> <code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html" linenums="false">
</code-example> </code-example>
_Interpolation_ is a convenient alternative to _property binding_ in many cases. 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*.
When rendering data values as strings, there is no technical reason to prefer one form to the other. ### Content security
You lean toward readability, which tends to favor interpolation.
You suggest establishing coding style rules and choosing the form that
both conforms to the rules and feels most natural for the task at hand.
When setting an element property to a non-string data value, you must use _property binding_. Imagine the following malicious content.
#### Content security <code-example path="property-binding/src/app/app.component.ts" region="malicious-content" header="src/app/app.component.ts" linenums="false">
Imagine the following *malicious content*.
<code-example path="template-syntax/src/app/app.component.ts" region="evil-title" header="src/app/app.component.ts" linenums="false">
</code-example> </code-example>
Fortunately, Angular data binding is on alert for dangerous HTML. In the component template, the content might be used with interpolation:
It [*sanitizes*](guide/security#sanitization-and-security-contexts) the values before displaying them.
It **will not** allow HTML with script tags to leak into the browser, neither with interpolation <code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.ts" linenums="false">
</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. nor property binding.
<code-example path="template-syntax/src/app/app.component.html" region="property-binding-vs-interpolation-sanitization" header="src/app/app.component.html" linenums="false"> 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" linenums="false">
</code-example> </code-example>
Interpolation handles the script tags differently than property binding but both approaches render the Interpolation handles the `<script>` tags differently than
content harmlessly. property binding but both approaches render the
content harmlessly. The following is the browser output
of the `evilTitle` examples.
<figure>
<img src='generated/images/guide/template-syntax/evil-title.png' alt="evil title made safe">
</figure>
```
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
"Template alert("evil never sleeps")Syntax" is the property bound evil title.
```
<hr/> <hr/>
{@a other-bindings} {@a other-bindings}
@ -855,7 +919,7 @@ table span attributes. They are pure attributes.
They do not correspond to element properties, and they do not set element properties. They do not correspond to element properties, and they do not set element properties.
There are no property targets to bind to. There are no property targets to bind to.
This fact becomes painfully obvious when you write something like this. This fact becomes obvious when you write something like this.
<code-example language="html"> <code-example language="html">
&lt;tr&gt;&lt;td colspan="{{1 + 1}}"&gt;Three-Four&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td colspan="{{1 + 1}}"&gt;Three-Four&lt;/td&gt;&lt;/tr&gt;