如何从父级向子级发出事件?

Posted

技术标签:

【中文标题】如何从父级向子级发出事件?【英文标题】:How to emit an event from parent to child? 【发布时间】:2017-10-18 14:11:06 【问题描述】:

我目前正在使用 Angular 2。通常我们使用@Output() addTab = new EventEmitter<any>(); 然后addTab.emit() 向父组件发出事件。

我们有什么办法可以做到这一点,从父母到孩子?

【问题讨论】:

只需将作为 Input 传递的表达式的值更改给子组件,子组件就会得到它(并由 ngOnChanges 通知)。您还可以使用具有 Observable 的共享服务发出事件。 【参考方案1】:

使用RxJs,你可以在你的父组件中声明一个Subject并将它作为Observable传递给子组件,子组件只需要订阅这个Observable

父组件

eventsSubject: Subject<void> = new Subject<void>();

emitEventToChild() 
  this.eventsSubject.next();

父 HTML

<child [events]="eventsSubject.asObservable()"> </child>    

子组件

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

ngOnInit()
  this.eventsSubscription = this.events.subscribe(() => doSomething());


ngOnDestroy() 
  this.eventsSubscription.unsubscribe();

【讨论】:

此外,数据可以使用这种方法与事件一起传递。比如,this.eventsSubject.next(data); 在父级中,然后this.events.subscribe((data) =&gt; doSomething(data)); 在子级中。 我已经编辑了这个很棒的答案以添加取消订阅。 +1 这里可能是一个初学者的问题:为什么要将eventsSubject 转换为 Observable 而不是仅仅将其作为主题订阅? 将 eventsSubject 转换为 Observable,防止子组件调用 next()。 感谢这个解决方案,它可以工作,但我在控制台中遇到错误:“ core.js:6185 ERROR TypeError: Cannot read property 'subscribe' of undefined” 错误指向子组件的 ngOnInit 中的 events.subscribe():“this.eventsSubscription = this.events.subscribe(() => doSomething());”我正在使用版本:“@angular/cli”:“~ 9.1.0”和“rxjs”:“~6.5.4”【参考方案2】:

据我所知,有两种标准方法可以做到这一点。

1. @输入

每当父项中的数据发生更改时,子项都会收到通知 ngOnChanges 方法。孩子可以采取行动。这是标准 与孩子互动的方式。

Parent-Component
public inputToChild: Object;

Parent-html
<child [data]="inputToChild"> </child>       

Child-Component: @Input() data;

ngOnChanges(changes:  [property: string]: SimpleChange )
   // Extract changes to the input property by its name
   let change: SimpleChange = changes['data']; 
// Whenever the data in the parent changes, this method gets triggered. You 
// can act on the changes here. You will have both the previous value and the 
// current value here.

    共享服务理念

创建服务并在共享服务中使用可观察对象。孩子 订阅它,只要有变化,就会通知孩子。这也是一种流行的方法。当您想要发送除了作为输入传递的数据之外的其他内容时,可以使用它。

SharedService
subject: Subject<Object>;

Parent-Component
constructor(sharedService: SharedService)
this.sharedService.subject.next(data);

Child-Component
constructor(sharedService: SharedService)
this.sharedService.subject.subscribe((data)=>

// Whenever the parent emits using the next method, you can receive the data 
in here and act on it.)

【讨论】:

我尝试了第一种方法,直到某个时候它都运行良好,然后我收到一条错误消息,指出值现在已更改,之前它是假的,现在它是真的。除此之外,它没有给我任何进一步的解释。 如果你的意思是 ExpressionChangedAfterItHasBeenCheckedError,这个错误不会在生产模式下抛出。 &lt;child [data]="inputToChild"&gt; &lt;/child&gt; 可能应该是 &lt;child [data]="(inputToChild)"&gt; &lt;/child&gt; 以获得更改 使用共享服务时,服务应该在组件类的元数据头中,而不是作为模块。模块服务是单例的,因此是全局范围的。存储在那里的任何变量都将持续存在并且是全局的,可能会导致缺陷并使错误难以发现。对配置有利,对大多数其他事情不利。【参考方案3】:

在父组件中,您可以使用@ViewChild() 来访问子组件的方法/变量。

@Component(
  selector: 'app-number-parent',
  templateUrl: './number-parent.component.html'
)
export class NumberParentComponent 
    @ViewChild(NumberComponent)
    private numberComponent: NumberComponent;
    increase() 
       this.numberComponent.increaseByOne();
    
    decrease() 
       this.numberComponent.decreaseByOne();
    
 

更新:

Angular 8 以后 -

@ViewChild(NumberComponent,  static: false )

【讨论】:

这似乎比使用 observables 更干净。缺点是孩子必须在视线范围内。例如,如果孩子加载了路由,这将失败,因为numberComponent 将是undefined 这是一个好习惯吗?我同意比 observables 更干净的事实,但我怀疑从父母那里操纵孩子的变量。无论如何,它非常适合我的需求,谢谢! 这是一个很好的提示。看起来@ViewChild 在 Angular 8 中发生了变化,或者至少我必须指定 ViewChild 的选项。我在我的例子中使用了这个:@ViewChild(Other, static: false ) private otherComponent: Other;【参考方案4】:

在子组件中使用 @Input() 装饰器以允许父组件绑定到此输入。

在子组件中,您按原样声明它:

@Input() myInputName: myType

要将属性从父级绑定到子级,您必须在模板中添加绑定括号和它们之间的输入名称。

例子:

&lt;my-child-component [myChildInputName]="myParentVar"&gt;&lt;/my-child-component&gt;

但要注意,对象是作为引用传递的,因此如果对象在子对象中更新,则父对象的 var 也会更新太多。 有时这可能会导致一些不需要的行为。 对于主要类型,值被复制。

要进一步阅读:

文档:https://angular.io/docs/ts/latest/cookbook/component-communication.html

【讨论】:

【参考方案5】:

在父级中,您可以使用@ViewChild 引用子级。需要时(即触发事件时),您可以使用 @ViewChild 引用从父级执行子级中的方法。

【讨论】:

【参考方案6】:

以前的解决方案都不适合我,因为我有一个嵌套递归子组件。所以我通过使用 OnChanges 函数使用了以下方法。

父组件

buttonToggle: boolean = false;

buttonClick(): void 
  this.buttonToggle = !this.buttonToggle;

父 HTML

<appNestedChildItemComponent [buttonTrigger]="buttonToggle"></appNestedChildItemComponent>

递归嵌套子组件

export class NestedChildItemComponent implements OnInit, OnChanges 

  @Input() buttonTrigger: boolean;
  buttonToggle: boolean = false;

  ngOnInit() 
    this.buttonToggle= buttonToggle;
  

  ngOnChanges(changes: SimpleChanges) 
    if (changes['buttonTrigger'] && changes['buttonTrigger']?.previousValue != changes['buttonTrigger']?.currentValue) 
      this.buttonToggle= !this.buttonToggle;
      // Do Something triggered by the parent button.
    
  

【讨论】:

以上是关于如何从父级向子级发出事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确使用 Angular 中的 Observable 将数组数据从父级发送到子级?

如何通过事件将数据从父级传递给子级

在 AngularJS 组件中从父级到子级通信事件

在laravel应用程序中从父组件向子组件发出vue事件而不使用事件总线

如何通过事件将数据从父级传递给子角色

Vue—子级向父级传递数据