【问题标题】:Create child components with NgFor then call a method of whichever emits an event使用 NgFor 创建子组件,然后调用发出事件的方法
【发布时间】:2017-04-18 10:06:29
【问题描述】:

我可以想出几种方法来破解这个问题,但我想要一些关于最'Angular'方法的建议。

我已经整理好了an example of what I'm doing

主组件使用NgFor 迭代列表并创建一堆子组件。
每个子组件在单击时会发出一个事件,该事件由父组件拾取。每个孩子还有一个方法update()

我想要的是让我的主要组件获取对发出事件的任何子组件的引用并调用其update() 方法的正确方法。

看起来很简单(可能很简单),但它是我正在构建的应用程序的一个核心功能,所以我希望它尽可能干净整洁。

为你的帮助干杯

【问题讨论】:

  • 我好像没看到你要做什么,为什么不直接在事件发出的时候调用update()方法呢?
  • 你是对的,在我的例子中这很明智,但我的实际应用程序更复杂。我试图简化并且可能走得太远了 - 我需要父级存储对发出事件的子级的引用。父母然后去做一些其他的事情,最后根据另一个触发器回调给孩子。我真的只需要一种方法来钩入发出事件的孩子,这样我就可以稍后再调用它。抱歉,我可能没有尽我所能地表达这个问题。
  • 好的,现在我明白了,所以我认为 JB Nizet 的答案就是你要找的。​​span>

标签: angular ngfor


【解决方案1】:

由于 Angular 2 采用单向数据流范式,我认为解决这个问题的“最‘Angular’方式”是调用update() 方法。子组件的状态应该由父组件提供。

每当点击发生时,新状态应在App(父)组件中计算,并将传递给ChildComponent

这样,您将需要通过附加属性(例如clicked)扩展父级中保存的状态:

rowList = [{name: "Alice", clicked: false}, ...];

(当然,由于undefinedfalse具有相同的逻辑值,因此不需要显式声明。)

单击时,在处理程序方法中,计算新状态。在这种情况下,它只是一个简单的布尔值更改:

processClick(id){
  this.recentClick = id;
  // Create the new array for immutability benefits.
  this.rowList = this.rowList.map(person => {
    if (person.name === id) {
      return {name: person.name, clicked: true};
    } else {
      return person;
    }
  })
}

该值会像任何其他@Input 一样传递给孩子。

<my-child *ngFor="let row of rowList;" [id]=row.name [clicked]=row.clicked (onMD)="processClick($event)"></my-child>

现在,子组件可以根据clicked 属性的值更改其行为。作为奖励,当使用不可变数据时,您可以将更改检测策略更改为onPush for some performance benefits

@Component({
    // ...
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
  @Input() clicked: boolean;

  get prefix() {
    return this.clicked ? "Bye!" : "Hi";
  }
  // ...
}

这种方法有很多好处。

  • 您没有隐藏在子组件中的状态。父母的“话语”是绝对的(所谓的“单一事实来源”)。
  • 父母并不真正需要知道或关心孩子如何处理数据或何时需要更新。
  • 您可以轻松地在其他地方提取数据,并且无需编写任何代码来使 update() 行为有效。

完整的实现如下所示:http://plnkr.co/edit/IP8iLKxKGCjHOdgPHJ0h?p=preview

【讨论】:

  • 我在看到这个之前接受了另一个答案,现在我很矛盾。接受的答案完全按照我的希望工作,并且是我所期待的那种解决方案,但是我看到了你接近的意义,并且可以看到它是一个比我从长远来看更清洁的解决方案。我想我可能会使用它,所以感谢您提供了一个非常好的、深入的答案。
【解决方案2】:

最简单的方法是使用模板变量来引用子组件:

<my-child #child *ngFor="let row of rowList;" [id]="row.name"
          (onMD)="processClick($event); child.update()"></my-child>

更新的演示:http://plnkr.co/edit/zA80iNsLHHULGx9Rbffw?p=preview

【讨论】:

  • 有没有办法像不使用*ngFor 时那样使用@ViewChild 之类的东西来引用控制器中的孩子?
  • 这很奏效。我可以将child 作为第二个参数传递给processClick(),并将其存储在我父母的一个变量中。稍后我可以使用它来回调被点击的任何孩子。一旦你知道怎么做就很简单了,正如我所怀疑的那样:)。感谢您的建议
  • 有ViewChildren。但这不会帮助您找到刚刚单击了哪个孩子(除了通过循环并找到事件中具有 id 的那个)
  • 我接受了这个答案,因为它以一种非常简单的方式完成了我想要实现的目标。我不得不说,@MateuszKocz 在下面给出的答案也很有意义,并且似乎值得进行少量的重构,因为它需要实现。如果您正在寻找与我相同的解决方案,那么我肯定会在您决定其中一个之前检查它们。
猜你喜欢
  • 2021-02-04
  • 1970-01-01
  • 2022-08-18
  • 2016-11-06
  • 2019-08-10
  • 2018-09-27
  • 2019-11-13
  • 2023-03-07
  • 1970-01-01
相关资源
最近更新 更多