kittencup / angular2-ama-cn

angular2 随便问
691 stars 101 forks source link

Angular 2 组件之间如何通信? #23

Open Coohack opened 8 years ago

Coohack commented 8 years ago

想要在组件之间共享数据,还有互相调用方法。依赖注入是单例的吗

kittencup commented 8 years ago

组件之间的共享可以有好几种方式

父->子 input 方式

import {Component,Input} from 'angular2/core';
@Component({
    selector: 'child',
    template: `
        <h2>child {{content}}</h2>
    `
})
class Child {
    @Input() content:string;
}

@Component({
    selector: 'App',
    directives: [Child],
    template: `
        <h1>App</h1>
        <child [content]="i"></child>
    `
})
export class App {

    i:number = 0;

    constructor() {
        setInterval(()=> {
            this.i++;
        }, 1000)
    }

}

子->父 output 方式

import {Output,EventEmitter,Component} from 'angular2/core';

@Component({
    selector: 'child',
    template: `
        <h2>child</h2>
    `
})
class Child {
    @Output() updateNumberI:EventEmitter<number> = new EventEmitter();
    i:number = 0;

    constructor() {
        setInterval(()=> {
            this.updateNumberI.emit(++this.i);
        }, 1000)
    }
}

@Component({
    selector: 'App',
    directives: [Child],
    template: `
        <h1>App {{i}}</h1>
        <child (updateNumberI)="numberIChange($event)"></child>
    `
})
export class App {

    i:number = 0;

    numberIChange(i:number){
        this.i = i;
    }

}

子获得父实例

如果不了解forwardRef用处的的可以看 https://github.com/kittencup/angular2-ama-cn/issues/11 @Host 表示这个Injector必须是host element在这里可以理解为 parent

import {Host,Component,forwardRef} from 'angular2/core';

@Component({
    selector: 'child',
    template: `
        <h2>child</h2>
    `
})
class Child {

    constructor(@Host() @Inject(forwardRef(()=> App)) app:App) {
        setInterval(()=> {
            app.i++;
        }, 1000);
    }
}

@Component({
    selector: 'App',
    directives: [Child],
    template: `
        <h1>App {{i}}</h1>
        <child></child>
    `
})
export class App {
    i:number = 0;
}

父获得子实例

子元素指令在父constructor时是获取不到的,所以必须在组件的ngAfterViewInit生命周期钩子后才能获取,如果对组件生命周期不了解的话,可以参考 https://github.com/kittencup/angular2-ama-cn/issues/56

import {ViewChild,Component} from 'angular2/core';

@Component({
    selector: 'child',
    template: `
        <h2>child {{i}}</h2>
    `
})
class Child {
    i:number = 0;
}

@Component({
    selector: 'App',
    directives: [Child],
    template: `
        <h1>App {{i}}</h1>
        <child></child>
    `
})
export class App {

    @ViewChild(Child) child:Child;
    ngAfterViewInit() {
        setInterval(()=> {
            this.child.i++;
        }, 1000)
    }

}

service 方式

import {Component,Injectable} from 'angular2/core';

@Injectable();
class KittencupService {
    i:number = 0;
}

@Component({
    selector: 'child',
    template: `
        <h2>child {{service.i}}</h2>
    `
})
class Child {

    constructor(public service:KittencupService){

    }
}

@Component({
    selector: 'App',
    directives: [Child],
    providers: [KittencupService],
    template: `
        <h1>App {{i}}</h1>
        <child></child>
    `
})
export class App {

    constructor(service:KittencupService) {
        setInterval(()=> {
            service.i++;
        }, 1000)
    }

}

service EventEmitter方式

import {Component,Injectable,EventEmitter} from 'angular2/core';

@Injectable()
class KittencupService {
    change: EventEmitter<number>;

    constructor(){
        this.change = new EventEmitter();
    }
}

@Component({
    selector: 'child',
    template: `
<h2>child {{i}}</h2>
`
})
class Child {

    public i:number = 0;

    constructor(public service:KittencupService){

        service.change.subscribe((value:number)=>{
            this.i = value;
        })
    }
}

@Component({
    selector: 'App',
    directives: [Child],
    providers: [KittencupService],
    template: `
<h1>App {{i}}</h1>
<child></child>
`
})
export class App {

    i:number = 0;

    constructor(service:KittencupService) {
        setInterval(()=> {
            service.change.emit(++this.i);
        }, 1000)
    }

}
hstarorg commented 8 years ago

service并不是单例,兄弟组件之间又该如何通信呢?

hstarorg commented 8 years ago

把服务放在最外层的根组件上,内部全部使用它,就变相的成了单例。

hstarorg commented 8 years ago

另外,请教下,如下做法是否可取。暂时还没有考虑取消订阅:

import {EventEmitter, Injectable} from 'angular2/core';

@Injectable()
export class EventBus {
  private callback: EventEmitter<any>;
  private subscriberObj: any;
  public constructor() {
    this.callback = new EventEmitter();
    this.subscriberObj = {};
  }

  public on(eventName, cb) {
    if (!this.subscriberObj.hasOwnProperty(eventName)) {
      this.subscriberObj[eventName] = [];
    }
    this.subscriberObj[eventName].push(cb);
    this.callback.subscribe((eventObj) => {
      if (this.subscriberObj.hasOwnProperty(eventObj.eventName)) {
        for (let fn of this.subscriberObj[eventObj.eventName]) {
          fn.apply(null, [eventObj.data]);
        }
      }
    });
  }

  public emit(eventName, data) {
    this.callback.emit({ eventName: eventName, data: data });
  }
}
pengkobe commented 7 years ago

话说哪几种方式比较好呢,或者适应的场景是什么?敬请大神回答,不慎感激

freyhill commented 7 years ago

很赞

oyboyb commented 6 years ago

兄弟组件是用什么通信呢?

jiayisheji commented 6 years ago

借助公共父级,服务,ngrx

oyboyb commented 6 years ago

看来也只能这样了,但你那个ngrx是什么鬼,没有理解到位

jiayisheji commented 6 years ago

ng专用状态管理,自行百度谷歌

oyboyb commented 6 years ago

3q

fattypiggy commented 6 years ago

@oyboyb 我觉得很好地一种方式就是同一父级引用各个兄弟的实例,然后每个子组建通知父组件,然后父组件再去通知子组件的兄弟, 就像 @jiayisheji 所说的公共父级, ngrx目前没有研究过,等研究一番后再来补充答案。