【问题标题】: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 的共享服务发出事件。

标签: angular typescript


【解决方案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.
}
  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.})

【讨论】:

  • 我尝试了第一种方法,直到某个时候它都运行良好,然后我收到一条错误消息,指出值现在已更改,之前它是假的,现在它是真的。除此之外,它没有给我任何进一步的解释。
  • 如果你的意思是 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.
          }
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-06
        • 1970-01-01
        • 2019-02-22
        • 2021-04-16
        • 2021-09-16
        • 2019-09-21
        • 2023-03-03
        • 2016-03-01
        相关资源
        最近更新 更多