场景
如上图,在一个歌曲详情模块,假设有2个子模块,歌曲模块和评论模块。
在歌曲模块和评论模块中都有评论数量这个属性,当用户在评论模块发布了一条评论后,评论模块和歌曲模块的数量要同步更新。
下面介绍几种实现方法。
1. 双向绑定实现
思路:
在AppComponent将评论数量count作为输入参数(@Input)传递给CommentComponent和MusicComponent,当AppComponent中的评论数量改变时,MucisComponent中的评论数量也会同时改变。
当CommentComponent更新评论数量count时,通过输出参数(@Output)更新AppComponent中的评论数量count,MusicComponent中的评论数量count就会跟着改变了。
AppComponent
- import { Component } from '@angular/core';
- @Component({
- selector: 'app-root',template: `<div>
- <app-music [count]="count"></app-music>
- <app-comment [(count)]="count"></app-comment>
- </div>`
- })
- export class AppComponent {
- count:number = 0;
- constructor() {
- }
- }
MusicComponent
CommentComponent
- import { Component,EventEmitter,Input,Output } from '@angular/core';
- @Component({
- selector: 'app-comment',template: `<div>
- <h3>评论</h3>
- <div>评论数:<strong>{{ count }}</strong></div>
- <div>
- <textarea #content></textarea>
- </div>
- <div>
- <button (click)="send(content)">发布</button>
- </div>
- <ul>
- <li *ngFor="let item of comments">
- {{ item }}
- </li>
- </ul>
- </div>`
- })
- export class CommentComponent {
- // 评论数
- @Input()
- count:number = 0;
- // 评论数改变事件
- @Output()
- countChange:EventEmitter<number> = new EventEmitter<number>();
- // 评论列表
- comments:string [] = [];
- constructor() { }
- // 发送评论
- send(content) {
- if (content.value) {
- this.comments.push(content.value);
- this.count++;
- this.countChange.emit(this.count);
- }
- }
- }
2. 观察者模式实现
思路
构建一个观察者对象,观察者对象拥有注册(regist),发布(fire),移除(remove)三个方法。在angular2中可以通过依赖注入注入到CommentComponent和MusicComponent。
在MusicComponent中注册(regist),‘count’ 事件。
当用户发送评论时,在CommentComponent中发布(fire),‘count’事件。
构造观察者对象
- import { Injectable } from '@angular/core';
- @Injectable()
- export class ObserverService {
- /**
- * 消息队列
- * 用数组保存每一种消息的事件队列,
- * eventList['count'] = []
- * 注册消息时将事件回调函数push到事件队列
- * eventList['count'].push(fn)
- * 发布消息时依次执行消息队列中的每一个回调函数
- * for(let fn of eventList['count']) {
- * fn(data);
- * }
- */
- eventList = {};
- constructor() {
- }
- /**
- * 发布消息
- * @param name 消息名
- * @param data 消息数据
- */
- fire(name:string,data:any) {
- if (this.eventList[name]) {
- const fns = this.eventList[name];
- for(let fn of fns) {
- fn(data);
- }
- }
- }
- /**
- * 注册消息
- * @param name
- * @param fn
- */
- regist(name:string,fn:any) {
- if (typeof fn === 'function') {
- const fns = this.eventList[name];
- if (fns) {
- fns.push(fn);
- } else {
- this.eventList[name] = [fn];
- }
- }
- }
- /**
- * 移除消息
- * @param name
- * @param fn
- */
- remove(name:string,fn:any) {
- const fns = this.eventList[name];
- if (fns) {
- if ( fn ) {
- let i;
- for(i = 0; i < fns.length; i++) {
- if (fns[i] === fn) {
- break;
- }
- }
- if (i < fns.length) {
- fns.splice(i,1);
- }
- } else {
- fns.length = 0;
- }
- }
- }
- }
AppComponent
- import { Component } from '@angular/core';
- @Component({
- selector: 'app-root',template: `<div>
- <app-music></app-music>
- <app-comment></app-comment>
- </div>`
- })
- export class AppComponent {
- constructor() {
- }
- }
MusicComponent
- import { ObserverService } from '../service/observer.service';
- import { Component,styleUrls: ['./music.component.css']
- })
- export class MusicComponent{
- // 评论数
- count:number;
- constructor(private observiceService:ObserverService) {
- // 注册消息
- this.observiceService.regist('count',count => this.count = count);
- }
- }
CommentComponent
- import { ObserverService } from '../service/observer.service.2';
- import { Component,template: `<div>
- <h3>评论</h3>
- <div>评论数:<strong>{{ count }}</strong></div>
- <div>
- <textarea #content></textarea>
- </div>
- <div>
- <button (click)="send(content)">发布</button>
- </div>
- <ul>
- <li *ngFor="let item of comments">
- {{ item }}
- </li>
- </ul>
- </div>`
- })
- export class CommentComponent {
- // 评论数
- count:number = 0;
- // 评论列表
- comments:string [] = [];
- constructor(private observiceService:ObserverService) { }
- send(content) {
- if (content.value) {
- this.comments.push(content.value);
- this.count++;
- // 发布消息
- this.observiceService.fire('count',this.count);
- }
- }
- }
3. 使用Rxjs中的Subject实现
subject参考
SubjectService
import { Observable,Subject } from 'rxjs/Rx';
import { Injectable } from '@angular/core';
@Injectable()
export class SubjectService {
- count:number = 0;
- comment:Subject<number>;
- constructor() {
- this.comment = new Subject<number>();
- }
}
AppComponent
- import { Component } from '@angular/core';
- @Component({
- selector: 'app-root',template: `<div>
- <app-music></app-music>
- <app-comment></app-comment>
- </div>`
- })
- export class AppComponent {
- constructor() {
- }
- }
MusicComponent
4. 小结
- 在使用双向绑定进行多个的模块间的数据传输时,可以看到,当需要传递某个变量时,我们需要在每个模块中构造对应的@Input和@Output,借助父模块来进行传递数据,过程比较繁琐。
AppComponent
CommentComponent
MusicComponent
MusicComponent
CommentComponent
- 借助Rxjs的Subject,我们能很方便的实现同样的功能。