父组件上的角度无限重渲染

时间:2019-05-07 20:43:13

标签: javascript angular

Stackblitz to reproduce issue

您好,我的Angular7应用包含一个父组件timesheet和一个子组件row。在此示例中,我希望timesheet为给定月份(在本例中为May)的每一天生成一个<app-row>组件。

现在,<app-row>组件仅由一个<td>元素组成,该元素带有一个接受数字的input字段。到目前为止,没有什么花哨的。

时间表组件:

<div *ngFor="let week of splitMonthIntoWeeks(getMonthDays(month))">
  <table>
    <thead>
      <tr>
        <th>
          Hours:
        </th>
      </tr>
    </thead>
    <tbody>
      <tr app-row *ngFor="let day of week"></tr>
    </tbody>
  </table>
</div>

行组件:

<td><input type="number"></td>

这很好用。但是,如果我尝试使用[(ngModel)]指令将每个行组件的输入元素链接到模型中的属性,则会得到该应用程序的无限重新渲染(您可以在上面的链接中进行复制。首先,它是可以正常工作,您必须将[(ngModel)]指令添加到行组件,如下所示。

总而言之,此更改提供了无限的重新渲染:

<td><input type="number" [(ngModel)]="hours"></td>

现在,我真的不知道Angular如何管理更改,也许我错过了一些东西,所以任何提示将不胜感激。

1 个答案:

答案 0 :(得分:1)

在此处查看解决方案

https://stackblitz.com/edit/angular-cjxqyt

您不应直接在模板中调用函数(splitMonthIntoWeeksgetMonthDays),因为它将在每个渲染周期被调用。这意味着将为每个调用生成一个包含新Date的新数组。 ngFor的工作原理是检查其值是否与上一个周期完全相同(使用===)。

如果比较不匹配,ngFor认为这是一个新对象,因此它将销毁相应的DOM元素并生成一个新的DOM元素。您可以在console.log组件构造函数中通过RowComponent看到它。

在比较两个复杂对象时,例如Date,它会检查该对象是否真的相同(内存中的指针相同),但是在您的情况下,它不是因为该函数不断重新创建一个新对象。

因此,您不必创建每个周期的函数,而可以创建数组并将结果持久保存在局部属性中,并仅在@Input month更改时重新计算此数组。为此,您可以使用onChanges的生命周期,也可以为@Input使用设置器(例如在我的示例中)。