如何从父母向孩子发出事件?


111

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

从父母到孩子,我们有什么方法可以做副手吗?


2
只需更改作为输入传递给子组件的表达式的值,子组件将获取它(并由ngOnChanges通知)。您也可以使用具有Observable的共享服务发出事件。
JB Nizet

Answers:


191

使用RxJs,您可以Subject在父组件中声明a 并将其作为Observable子组件传递,子组件只需要订阅this 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();
}

13
同样,可以使用此方法将数据与事件一起传递。就像this.eventsSubject.next({data});在父母,然后this.events.subscribe(({data}) => doSomething(data));在孩子。

2
我已经编辑了这个很棒的答案以添加取消订阅。+1
Umar Farooq Khawaja '18

1
这里可能是一个初学者的问题:为什么要转换eventsSubject为Observable而不是仅将其订阅为Subject?
贾斯汀·摩根

11
将eventsSubject转换为Observable,防止子组件调用next()。
BlueNC

2
感谢您使用此解决方案,但它在控制台中出现错误:“ core.js:6185错误TypeError:无法读取未定义的属性'subscribe'”该错误指向ngOnInit中的events.subscribe()子组件的名称:“ this.eventsSubscription = this.events.subscribe(()=> doSomething());”我正在使用以下版本:“ @ angular / cli”:“〜9.1.0”和“ rxjs” :“〜6.5.4”
爱德华多

71

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

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.
}
  1. 共享服务理念

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

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.})

我尝试了第一种方法,直到某些时候它都正常工作,之后我收到一条错误消息,指出现在更改了值,更早的时候它是假的,现在是真实的。除此之外,它没有给我任何进一步的解释。
代码

2
假设您的意思是ExpressionChangedAfterItHaHasBeenCheckedError,则不会在生产模式下引发此错误。
user1337

<child [data]="inputToChild"> </child>应该可能是<child [data]="(inputToChild)"> </child>要得到改变
pmc

28

在父组件中,可以使用@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 })

4
这似乎比使用可观察对象干净。缺点是孩子必须在视野中。如果孩子被加载例如路由,这将失败,因为numberComponentundefined
Shy Agam

1
这是一个好习惯吗?我同意这样的事实,即它比可观察的事物更清洁,但是我怀疑是否要从父母那里操纵孩子的变量。无论如何,它非常适合我的需求,谢谢!
塔卡塔尔维

1
这是一个很好的提示。看起来@ViewChild在Angular 8中已更改,或者至少我必须指定ViewChild的选项。我在我的情况下使用了此方法:@ViewChild(Other,{static:false})private otherComponent:其他;
Tore Aurstad

8

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

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

@Input() myInputName: myType

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

范例:

<my-child-component [myChildInputName]="myParentVar"></my-child-component>

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

要进一步阅读此内容:

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


1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.