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: `
      

当前值: {{ count }}

` }) 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: `
   
  `
})
export class AppComponent {
  initialCount: number = 5;
}

输出属性 (子组件 -> 父组件)

counter.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'exe-counter',
    template: `
      

当前值: {{ count }}

` }) export class CounterComponent { @Input() count: number = 0; @Output() change: EventEmitter = new EventEmitter(); 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: `
   

{{changeMsg}}

` }) 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: `
    
    
  `
})

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: `
      

Child Component

` }) 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: `
    

Welcome to Angular World

`, }) export class AppComponent { @ViewChild(ChildComponent) childCmp: ChildComponent; ngAfterViewInit() { this.childCmp.name = 'child-component'; } }

使用 MessageService - 基于 RxJS Subject

message.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();

    sendMessage(message: string) {
        this.subject.next({ text: message });
    }

    clearMessage() {
        this.subject.next();
    }

    getMessage(): Observable {
        return this.subject.asObservable();
    }
}

home.component.ts

import { Component } from '@angular/core';
import { MessageService } from './message.service';

@Component({
    selector: 'exe-home',
    template: `
    

Home

` }) 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: `
    
{{message.text}}
` }) 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 对象中 $on$broadcast 的功能。

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;

  constructor() {
    this._eventBus = new Subject();
  }

  broadcast(key: any, data?: any) {
    this._eventBus.next({key, data});
  }

  on(key: any): Observable {
    return this._eventBus.asObservable()
      .filter(event => event.key === key)
      .map(event => event.data);
  }
}

child.component.ts

import { Component } from '@angular/core';

@Component({
    selector: 'child'
})
export class ChildComponent {
  constructor(private broadcaster: Broadcaster) {}
  
  registerStringBroadcast() {
    this.broadcaster.on('MyEvent')
      .subscribe(message => {
        ...
      });
  }

  emitStringBroadcast() {
    this.broadcaster.broadcast('MyEvent', 'some message');
  }
}

本文主要是介绍组件通讯的思路,提供的都是相对简单的示例。如果想深入了解,请参考 - angular.cn - component-communication。

我有话说

1.在实际开发中,我们也经常使用 Pub (发布) - Sub (订阅模式) 来实现模块之间的消息通讯。接下来我们看一下 Pub - Sub 的核心点:

  • 至少包含 subscribe() 和 publish() 两个方法,subscribe() 用于实现消息订阅,publish() 方法用于发布消息。

  • 支持订阅不同的消息类型,且对于任何一种消息类型都可以添加多个观察者。内部实现一般使用 key-value 结构进行消息类型和观察者列表的存储。

  • 订阅观察者后,最好返回一个对象或函数对象,用于取消订阅。

具体示例如下(详细信息,请参考 - 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', x),
    error => console.log('B Error:' + error)
);

subject.subscribe(
    x => console.log('C', x),
    error => console.log('C Error:' + error)
);

source.subscribe(subject);

关于 RxJS Subject 的详细信息,请查看 - RxJS Subject。

你可能感兴趣的:(typescript,angular.js,angular2)