如何从父级向子级发出事件?
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) => 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,这个错误不会在生产模式下抛出。<child [data]="inputToChild"> </child>
可能应该是 <child [data]="(inputToChild)"> </child>
以获得更改
使用共享服务时,服务应该在组件类的元数据头中,而不是作为模块。模块服务是单例的,因此是全局范围的。存储在那里的任何变量都将持续存在并且是全局的,可能会导致缺陷并使错误难以发现。对配置有利,对大多数其他事情不利。【参考方案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
要将属性从父级绑定到子级,您必须在模板中添加绑定括号和它们之间的输入名称。
例子:
<my-child-component [myChildInputName]="myParentVar"></my-child-component>
但要注意,对象是作为引用传递的,因此如果对象在子对象中更新,则父对象的 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 将数组数据从父级发送到子级?