【问题标题】:Child component events broadcast to parent子组件事件广播给父组件
【发布时间】:2016-04-20 12:34:39
【问题描述】:

我想实现常见的 Angular 1.x 模式,即在 Angular 2 的父指令中包含子指令。这是我想要的结构。

<foo>
  <bar>A</bar>
  <bar>B</bar>
  <bar>C</bar>
</foo>

我希望这些 Bar 组件具有发送到 Foo 组件的 click 事件。

到目前为止,这是我的Foo

@Component({
  selector: 'foo',
  template: `
    <div>
      <ng-content></ng-content>
    </div>
  `
})
export class Foo {
   @ContentChildren(Bar) items: QueryList<Bar>;
}

这是我的Bar

@Component({
  selector: 'Bar',
  template: `
    <div (click)="clickity()">
      <ng-content></ng-content>
    </div>
  `
})
export class Bar {
  clickity() {
    console.log('Broadcast this to the parent please!');
  }
}

Foo 的其中一个Bars 被点击时,我该如何通知它?

【问题讨论】:

  • 您可以使用 EventEmitter 方法在 Bar 中使用 .emit(),在 Foo 组件中使用 .subscribe()。作为@Output() 或通过服务,如this example
  • @Sasxa 你能澄清一下吗?我知道如何从子组件到父组件使用@Output(),但前提是我可以实际将属性放在子组件上。我不知道如何使用用户定义的孩子动态地做到这一点。
  • tbh,我没有那么多使用@Output()...我更喜欢该服务,我在两个类中都导入它,请求相同的发射器实例并在我需要的地方注册发射/订阅,我将尝试在几分钟内创建示例...
  • 看看this example。不管组件是如何创建的,只需在它们的文件中导入服务......

标签: typescript angular angular2-directives


【解决方案1】:

如果您不能使用@Output() 装饰器,您可以使用服务在组件之间发送数据。这是一个例子:

import {EventEmitter} from 'angular2/core';

export class EmitterService {
  private static _emitters: { [channel: string]: EventEmitter<any> } = {};
  static get(channel: string): EventEmitter<any> {
    if (!this._emitters[channel]) 
      this._emitters[channel] = new EventEmitter();
    return this._emitters[channel];
  }
}

您可以在需要发出或订阅事件的任何地方导入它:

// foo.component.ts
import {EmitterService} from '../path/to/emitter.service'

class Foo {
  EmitterService.get("some_id").subscribe(data => console.log("some_id channel: ", data));
  EmitterService.get("other_id").subscribe(data => console.log("other_id channel: ", data));
}

// bar.component.ts
import {EmitterService} from '../path/to/emitter.service'

class Bar {

  onClick() {
    EmitterService.get("some_id").emit('you clicked!');
  }
  onScroll() {
    EmitterService.get("other_id").emit('you scrolled!');
  }
}

另一个例子:plunker

【讨论】:

    【解决方案2】:

    为什么不使用@ContentChildern?

    在 bar.component.ts 中我们暴露了点击事件

    @Output() clicked = new EventEmitter<BarComponent>();
    onClick(){
        this.clicked.emit(this);
    }
    

    在 foo.component.ts 中我们订阅每个的 clicked 事件

     @ContentChildren(BarComponent) accordionComponents: QueryList<BarComponent>;
    
     ngAfterViewInit() {
     this.accordionComponents.forEach((barComponent: BarComponent) => {
            barComponent.clicked.subscribe((bar: BarComponent) => doActionsOnBar(bar));           
        });
    }
    

    【讨论】:

      【解决方案3】:

      另一个答案在解决问题方面做得很差。 EventEmitters 只能与 @Outputs 一起使用,并且这个问题没有利用 Angular 2 内置的依赖注入或 RxJS 的特性。

      具体来说,如果不使用 DI,你会强迫自己进入一个场景,如果你重用依赖于静态类的组件,它们都会收到相同的事件,而这可能是你不想要的。

      请看下面的例子,利用 DI,很容易多次提供同一个类,使使用更加灵活,同时避免了对有趣的命名方案的需要。如果你想要多个事件,你可以使用不透明的标记提供这个简单类的多个版本。

      工作示例: http://plnkr.co/edit/RBfa1GKeUdHtmzjFRBLm?p=preview

      // The service
      import 'rxjs/Rx';
      import {Subject,Subscription} from 'rxjs/Rx';
      
      export class EmitterService {
        private events = new Subject();
        subscribe (next,error,complete): Subscriber {
          return this.events.subscribe(next,error,complete);
        }
        next (event) {
          this.events.next(event);
        }
      }
      
      @Component({
        selector: 'bar',
        template: `
          <button (click)="clickity()">click me</button>
        `
      })
      export class Bar {
        constructor(private emitter: EmitterService) {}
        clickity() {
          this.emitter.next('Broadcast this to the parent please!');
        }
      }
      
      @Component({
        selector: 'foo',
        template: `
          <div [ngStyle]="styl">
            <ng-content></ng-content>
          </div>
        `,
        providers: [EmitterService],
        directives: [Bar]
      })
      export class Foo {
        styl = {};
        private subscription;
        constructor(private emitter: EmitterService) {
          this.subscription = this.emitter.subscribe(msg => {
            this.styl = (this.styl.background == 'green') ? {'background': 'orange'} : {'background': 'green'};
          });
        }
        // Makes sure we don't have a memory leak by destroying the
        // Subscription when our component is destroyed
        ngOnDestroy() {
          this.subscription.unsubscribe();
        }
      }
      
      猜你喜欢
      • 1970-01-01
      • 2022-11-24
      • 1970-01-01
      • 1970-01-01
      • 2021-11-07
      • 2013-05-13
      • 1970-01-01
      • 1970-01-01
      • 2016-03-01
      相关资源
      最近更新 更多