*使用函数的*ngFor返回一个循环

7
当我在Angular中使用返回数据的函数来使用*ngFor时,该函数会被多次调用,有时甚至会导致循环:

app.component.ts

export class AppComponent {

 getArray(): string[] {

   //here i know when this function is called
   console.log('getArray called')

   return ['number one', 'number two']
 }

}

app.component.html

<h1 *ngFor="let item of getArray()">
 {{ item }}
</h1>

我的控制台:

enter image description here

然后我发现函数getArray()被多次调用,但我不知道为什么。


在我的 app.component.html 文件中,第一行。 - outrowender
3个回答

10

更新

Angular在每个变更检测周期中都会评估你在模板中使用的所有表达式,因此它被称为多次时间。变更检测周期始于ApplicationRef.tick方法。

当应用程序启动时,它会立即调用该tick方法,然后由ngZone.onMicrotaskEmpty订阅进行管理。

此外,每个tick方法还会在开发模式下执行额外的检查checkNoChanges

因此,你会得到:

App starts
   loadComponent
      tick
        checkChanges
              evaluate getArray()
        checkNoChanges
              evaluate getArray()
  ngZone.onMicrotaskEmpty
      subscribe(all promised have been executed)
         tick
           checkChanges
              evaluate getArray()
           checkNoChanges
              evaluate getArray()

      ...some time later
      subscribe(you click somewhere)
         tick
           checkChanges
              evaluate getArray()
           checkNoChanges
              evaluate getArray()
      subscribe(you make a http request)
         tick
           checkChanges
              evaluate getArray()
           checkNoChanges
              evaluate getArray()

之前的回答

在Angular模板中,应避免使用表达式执行复杂计算、进行副作用或在每次变更检测时返回新值。

特别是在你的代码中。

<h1 *ngFor="let item of getArray()">

在每次模板检查时,你都会返回一个新的数组。如果你的项目是对象,那么ngForOf指令会检测到你已更改数组并尝试重新渲染它。

最好在代码中定义该数组一次。

arr = ['number one', 'number two']

<h1 *ngFor="let item of arr">

ngForOf指令的另一种可行方式是使用trackBy,但最好在项中有一些唯一键。

另请参见


这解释了循环。我将改变我的代码策略。 - outrowender
1
这个 arr 应该在哪里创建? - lesolorzanov

2
@Yurzui的回答实际上并不完全正确。这里有一个例子:https://stackblitz.com/edit/angular-uqahdx 它被多次调用是因为Angular生命周期钩子的工作方式。这是当所有的生命周期钩子都被挂起时页面加载的console.log:
ngOnInit
ngDoCheck                       <!-- Detect and act upon changes that Angular can't or won't detect on its own.
ngAfterContentInit
ngAfterContentChecked           <!-- This is where the *ngFor ng-template is injected and getArray() is evaluated.
!>   getArray called               
ngAfterViewInit
ngAfterViewChecked              <!-- Angular ensures that the data hasn't changed between when the view "compilation" started and ended.
!>   getArray called               
ngDoCheck                       <!-- Angular then does an immediate pass over data bound elements 
ngAfterContentChecked           <!-- Angular has to call getArray yet again because the array reference in memory has changed as we are returning a new array. (like what @Yurzui said)
!>   getArray called               
ngAfterViewChecked              <!-- Angular runs the checking process again. This is where people get that "ExpressionChangedAfterItHasBeenCheckedError" error.
!>   getArray called            

正如您所看到的,这些日志与您的屏幕截图相匹配,其中包含4个对getArray()的调用。

-1

如果你想在你的"item"中进行操作,你可以使用Angular管道


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接