Angular 2 Components Communicate
本文介绍的内容是组件通信的常用方式:@Input、@Output、@ViewChild、模板变量、MessageService、Broadcaster (Angular 1.x $rootScope 中 $on、$broadcast ) 和 Pub - Sub 模式、RxJS Subject 存在的问题。 输入属性 (父组件 -> 子组件)counter.component.ts import { Component,Input } from '@angular/core'; @Component({ selector: 'exe-counter',template: ` <p>当前值: {{ count }}</p> <button (click)="increment()"> + </button> <button (click)="decrement()"> - </button> ` }) export class CounterComponent { @Input() count: number = 0; increment() { this.count++; } decrement() { this.count--; } } app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'exe-app',template: ` <exe-counter [count]="initialCount"></exe-counter> ` }) export class AppComponent { initialCount: number = 5; } 输出属性 (子组件 -> 父组件)counter.component.ts import { Component,Input,Output,EventEmitter } from '@angular/core'; @Component({ selector: 'exe-counter',template: ` <p>当前值: {{ count }}</p> <button (click)="increment()"> + </button> <button (click)="decrement()"> - </button> ` }) export class CounterComponent { @Input() count: number = 0; @Output() change: EventEmitter<number> = new EventEmitter<number>(); increment() { this.count++; this.change.emit(this.count); } decrement() { this.count--; this.change.emit(this.count); } } app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'exe-app',template: ` <p>{{changeMsg}}</p> <exe-counter [count]="initialCount" (change)="countChange($event)"></exe-counter> ` }) export class AppComponent { initialCount: number = 5; changeMsg: string; countChange(event: number) { this.changeMsg = `子组件change事件已触发,当前值是: ${event}`; } } 模板变量child.component.ts import {Component} from '@angular/core'; @Component({ selector: 'child-component',template: `I'm {{ name }}` }) export class ChildComponent { public name: string; } parent.component.ts import {Component,OnInit} from '@angular/core'; import {ChildComponent} from './child-component.ts'; @Component({ selector: 'parent-component',template: ` <child-component #child></child-component> <button (click)="child.name = childName">设置子组件名称</button> ` }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { this.childName = 'child-component'; } } @ViewChild 装饰器child.component.ts import { Component,OnInit } from '@angular/core'; @Component({ selector: 'exe-child',template: ` <p>Child Component</p> ` }) export class ChildComponent { name: string = ''; } app.component.ts import { Component,ViewChild,AfterViewInit } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ selector: 'my-app',template: ` <h4>Welcome to Angular World</h4> <exe-child></exe-child> `,}) export class AppComponent { @ViewChild(ChildComponent) childCmp: ChildComponent; ngAfterViewInit() { this.childCmp.name = 'child-component'; } } 使用 MessageService - 基于 RxJS Subjectmessage.service.ts import { Injectable } from '@angular/core'; import {Observable} from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; @Injectable() export class MessageService { private subject = new Subject<any>(); sendMessage(message: string) { this.subject.next({ text: message }); } clearMessage() { this.subject.next(); } getMessage(): Observable<any> { return this.subject.asObservable(); } } home.component.ts import { Component } from '@angular/core'; import { MessageService } from './message.service'; @Component({ selector: 'exe-home',template: ` <div> <h1>Home</h1> <button (click)="sendMessage()">Send Message</button> <button (click)="clearMessage()">Clear Message</button> </div>` }) export class HomeComponent { constructor(private messageService: MessageService) {} sendMessage(): void { this.messageService.sendMessage('Message from Home Component to App Component!'); } clearMessage(): void { this.messageService.clearMessage(); } } app.component.ts import { Component,OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs/Subscription'; import { MessageService } from './message.service'; @Component({ selector: 'my-app',template: ` <div> <div *ngIf="message">{{message.text}}</div> <exe-home></exe-home> </div> ` }) export class AppComponent implements OnDestroy { message: any; subscription: Subscription; constructor(private messageService: MessageService) { this.subscription = this.messageService .getMessage().subscribe( message => { this.message = message; }); } ngOnDestroy() { this.subscription.unsubscribe(); } } 使用 Broadcaster - 基于 RxJS Subject实现 Angular 1.x 中的 $rootScope 对象中 broadcaster.ts import { Injectable } from '@angular/core'; import {Subject} from 'rxjs/Subject'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/filter'; import 'rxjs/add/operator/map'; interface BroadcastEvent { key: any; data?: any; } @Injectable() export class Broadcaster { private _eventBus: Subject<BroadcastEvent>; constructor() { this._eventBus = new Subject<BroadcastEvent>(); } broadcast(key: any,data?: any) { this._eventBus.next({key,data}); } on<T>(key: any): Observable<T> { return this._eventBus.asObservable() .filter(event => event.key === key) .map(event => <T>event.data); } } child.component.ts import { Component } from '@angular/core'; @Component({ selector: 'child' }) export class ChildComponent { constructor(private broadcaster: Broadcaster) {} registerStringBroadcast() { this.broadcaster.on<string>('MyEvent') .subscribe(message => { ... }); } emitStringBroadcast() { this.broadcaster.broadcast('MyEvent','some message'); } } 本文主要是介绍组件通讯的思路,提供的都是相对简单的示例。如果想深入了解,请参考 - angular.cn - component-communication。 我有话说1.在实际开发中,我们也经常使用 Pub (发布) - Sub (订阅模式) 来实现模块之间的消息通讯。接下来我们看一下 Pub - Sub 的核心点:
具体示例如下(详细信息,请参考 - Pub/Sub JavaScript Object): var events = (function(){ var topics = {}; var hOP = topics.hasOwnProperty; return { subscribe: function(topic,listener) { // 如果topic类型不存在,则创建 if(!hOP.call(topics,topic)) topics[topic] = []; // 添加listener var index = topics[topic].push(listener) -1; // 返回对象用于移除listener return { remove: function() { delete topics[topic][index]; } }; },publish: function(topic,info) { if(!hOP.call(topics,topic)) return; topics[topic].forEach(function(item) { item(info != undefined ? info : {}); }); } }; })(); 使用示例: var subscription = events.subscribe('/page/load',function(obj) { // 事件处理 }); events.publish('/page/load',{ url: '/some/url/path' }); 2.RxJS Subject 在使用中存在一个问题,就是如果某个 observer (观察者) 在执行的时候出现异常,却没有进行异常处理,那么就会影响到其它的观察者。解决该问题,最简单的方式就是为所有的观察者添加异常处理。具体问题如下: const source = Rx.Observable.interval(1000); const subject = new Rx.Subject(); const example = subject.map(x => { if (x === 1) { throw new Error('oops'); } return x; }); subject.subscribe(x => console.log('A',x)); example.subscribe(x => console.log('B',x)); subject.subscribe(x => console.log('C',x)); source.subscribe(subject); 以上代码运行后,控制台的输出结果: A 0 B 0 C 0 A 1 Rx.min.js:74 Uncaught Error: oops 解决方案: const source = Rx.Observable.interval(1000); const subject = new Rx.Subject(); const example = subject.map(x => { if (x === 1) { throw new Error('oops'); } return x; }); subject.subscribe( x => console.log('A',x),error => console.log('A Error:' + error) ); example.subscribe( x => console.log('B',error => console.log('B Error:' + error) ); subject.subscribe( x => console.log('C',error => console.log('C Error:' + error) ); source.subscribe(subject); 关于 RxJS Subject 的详细信息,请查看 - RxJS Subject。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |