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,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));