当我在带有返回我的数据的函数的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>
我的控制台:
然后我多次调用函数getArray(),我不知道为什么。
答案 0 :(得分:5)
您看到它称为乘以时间,因为Angular在每个更改检测周期都会评估您在模板中使用的所有表达式。更改检测周期以ApplicationRef.tick方法开始。
应用程序启动时立即calls那个滴答方法,然后由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,但最好在项目中使用一些唯一的键。
另请参见
答案 1 :(得分:2)
@Yurzui的答案实际上并非完全正确。这是一个示例:https://stackblitz.com/edit/angular-uqahdx
由于角度lifecycle hooks的工作方式而被多次调用。这是所有生命周期挂钩均已安装时页面加载的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
如您所见,这些日志与您对getArray()
的4次调用的屏幕截图保持一致。