五:Angular 数据绑定 (Data Binding)
通常来说,数据绑定要么是从页面流向组件中的数据,要么是从组件中的数据流向页面。下面我们来介绍在Angular 2中数据绑定的几种不同方式。
1. 使用{{}}将组件中的数据显示在html页面上
实现方式:<div>{{value}}</div>
这样就可将组件中的value值显示在div元素上。
2. 使用[]DOM元素的属性值设置为组件中的数据
实现方式:<img [src]="srcUrl">
这样就可以将img标签的src属性设置为组件中的srcUrl值。但是使用[]只能绑定在DOM元素已有的属性上,例如<div [src]="srcUrl"></div>
是会报错的,以为div不存在src属性。
但是,可以通过自定义的方式使用@Input为我们的指令添加属性,这也是实现从父子组件数据通信的一种方式,顾名思义就是@Input定义的属性值是从父组件中获取的。
下面我们来介绍使用@Input实现数据绑定。
//父组件 @Component({ selector: 'my-app', template: `<customer-counter [counter] = "someValue"></customer-counter>`, directives:[CustomerCounterComponent] }) export class AppComponent{ someValue = 3; } //子组件 @Component({ selector: 'customer-counter', template: `<span>{{counter}}</span>` }) export class CustomerCounterComponent{ counterValue = 0; @Input() get counter(){ return this.counterValue; } }
这样<customer-counter [counter] = "someValue"></customer-counter>
就可以将父组件中的someValue数据绑定在子组件的counter属性上了。
3. 事件绑定
事件绑定就是通过用户的交互行为来触发DOM元素的事件。
例如:<button (click)="onClickMe()">点我!</button>
当用户点击button是就会触发onClickMe()方法,这样实现的前提是button能够监听click方法。
当然,也可以通过@Output在我们的组件中添加自定义事件,@Output顾名思义就是在子组件中向父组件输出东西。
具体实现如下:
//父组件 @Component({ selector: 'my-app', template: `<customer-counter (counterChange) = "changeValue($event)"></customer-counter> <div>{{someValue}}</div>`, directives:[CustomerCounterComponent] }) export class AppComponent{ someValue = 3; changeValue(val){ this.someValue = val; } } //子组件 @Component({ selector: 'customer-counter', template: `<button (click)="decrement()">-</button>` }) export class CustomerCounterComponent{ counterValue = 100; @Output() counterChange = new EventEmitter(); decrement() { this.counterChange.emit(-- this.counterValue ); } }
这样<customer-counter (counterChange) = "changeValue($event)"></customer-counter>
当子组件中的counterChange事件被触发,就会执行父组件中的changeValue()方法,进而改变父组件中的相关数据。
4. 双向绑定
上面介绍的方式{{}}和[]是将组件中的数据绑定到DOM元素上,而()是将组件中的事件绑定到组件中的方法上,那么该如何数据的双向绑定呢?
Angular2是通过[()]来实现的,例如<input [(ngModel)]="value">
就是双向绑定input元素的值。但是需要注意的是[()]只能绑定在有输入输出功能的DOM元素上(例如:input、textare),如果绑定在div这样的元素上就会报错。
那么,如何通过[()]在我们自定义的指令上实现双向绑定呢?没错,就是使用@Input和@Output来实现。
//父组件 @Component({ selector: 'my-app', template: ` <customer-counter [(counter)] = "someValue"></customer-counter> <p> value: {{someValue}}</p>`, directives:[CustomerCounterComponent] }) export class AppComponent{ someValue = 3; } //子组件 @Component({ selector: 'customer-counter', template: `<button (click)="decrement()">-</button> <span>{{counter}}</span>` }) export class CustomerCounterComponent{ counterValue = 0; @Input() get counter(){ return this.counterValue; } @Output() counterChange = new EventEmitter(); set counter(val) { this.counterValue = val; this.counterChange.emit(this.counterValue); } decrement() { this.counter--; } }
这样<customer-counter [(counter)] = "someValue"></customer-counter>
就可以将父组件中的someValue绑定到子组件的counter属性上,同时当子组件的counter属性发生改变时也更新父组件的someValue值。
需要注意的是,我们定义的事件监听是counterChange,而使用的确实[(counter)]。这是因为Angular 2中约定添加后缀Change,也就是[(counter)]等价于[change]和(counterChange)的组合。如果去看[(ngModel)]的实现,你也会发现它是[ngModel]和[ngModelChange]的组合。