在前面的学习中了解了angular2中组件的数据流,在组件间我们可以利用input、output实现组件间的数据传递。再来回顾一下前面的应用场景,一辆车(car)包含了引擎(engine)、门(door)等,车是父组件,门和引擎作为子组件。我们可以在引擎中加入一个启动状态(engineStatus),当引擎启动时我们需要告知车,那么我们需要在engine组件中output一个事件(EngineStatusChanged),并在engineStatus状态改变时发布event,而父组件car需要在订阅这个引擎的事件[EngineStatusChanged]=”onEngineStatusChanged($event)”,通过$event参数我们在car组件中得到了engine的engineStatus,实现了子组件向父组件的数据传递,接下来如果车门(door)要通过了解引擎状态来改变车门的状态该如何处理呢?当然需要在door中加入一个input属性用来接收car组件的绑定,实现父组件向子组件的数据传递。
从这个应用场景看,engine中的engineStatus是其一个属性,它可以由它的使用者car读取或更改,而无法让与它无关的door直接进行访问,这以为着这个值的变化是以树状结构进行扩散,那么如果在这个树上的一个节点访问另一个节点的一个属性将会导致在这两个节点间的所有节点(根节点除外)都将要output、input这个属性。如果我们的组件设计的扁平化,那么这个问题到不大,但如果组件树有一定深度后,这个效率就明显不足了。
再仔细分析这个应用不难发现这是一个典型的观察者模式应用,在angular2 中我们可以利用服务来实现这个模式,我们可以建一个服务,在服务中建立的一个观察对象engineStatus,同时提供一个接口可以实现对更改这个对象的状态,并将变化推送出去,观察者订阅这个观察对象。查看示例:
Car.service.ts
/**
* Created by Administrator on2016-06-16.
*/
import {Injectable} from "@angular/core";
import {Observable, Observer} from "rxjs/Rx";
@Injectable()
export class CarService{
engineStatus:Observable<boolean>;
private observer:Observer<boolean>;
constructor(){
this.engineStatus= new Observable<boolean>(observer=>this.observer=observer).share();
}
changeEngineStatus(newstatus:boolean){
if(this.observer!==undefined)
this.observer.next(newstatus);
}
}
在这个服务中engineStatus是观察对象,我们同时定义了一个observer用于推送状态。changeEngineStatus方法是该服务对外公布的一个用于改变观察对象的一个方法,通过this.observer.next(newstatus)推送一个新的状态。
Engine.ts
/**
* Created by Administrator on2016-06-16.
*/
import {Component} from "@angular/core";
import {CarService} from"./car.service";
@Component({
selector:'engine',
template:`
<div style="background: rebeccapurple">
<p>this is engine</p>
<button (click)="onFired()">fire</button>
<button (click)="onUnFired()">unFire</button>
</div>
`
})
export class engine
{
constructor(private carService:CarService){
}
onFired()
{
this.carService.changeEngineStatus(true);
}
onUnFired()
{
this.carService.changeEngineStatus(false);
}
}
通过注入服务,由事件去调用服务中公布的方法去改变观察对象的状态。
Door.ts
/**
* Created by Administrator on2016-06-16.
*/
import {Component} from "@angular/core";
import {CarService} from"./car.service";
@Component({
selector:'door',
template:`
<div style="background: gray">
<p>this is door</p>
<p *ngIf="engineStatus" >engine fired</p>
</div>
`
})
export class door{
engineStatus:boolean = false;
subscription:any;
constructor(private carService:CarService){
}
ngOnInit(){
this.subscription=this.carService.engineStatus.subscribe(status=>{this.engineStatus=status;});
}
ngOnDestroy(){
this.subscription.unsubscribe();
}
}
ngOnInit即是组件加载时实现对服务中engineStatus的订阅 ,这样只要服务推送一个值,那么我们就可以通过这个订阅 来实现获取服务推送过来的值。
在这个例子中carService作为被观察者,提供了一个观察对象,同时提供了用于更新对象的接口,外部使用者只需订阅这个观察对象就可以获取对象的值。同时我们会发现在这个示例中由于外部使用者相互间并不知道彼此的存在,这样也会造成被观察对象能够被任意的更新,使用者也不知道是谁引发的改变,可能会导致一些逻辑上的混乱,在实际应用开发中就需要注意加入一些逻辑上的判断。
通过这种方式,能够在组件间很容易实现数据的传递,这种模式在angular2 内部就有大量的应用,其中最典型的就是http服务,这种模式更容易进行系统设计并且便于维护扩展。