Angular2: 嵌套的 *ngFor 导致了 'Expression has changed after it was checked' 错误。
Angular2: 嵌套的 *ngFor 导致了 'Expression has changed after it was checked' 错误。
我有一个Angular2组件'my-tree',我在父组件'my-app'中使用它。
'my-app'的代码如下:
@Component({
selector: 'my-app',
providers: [],
template: `
`,
directives: [MyTree]
})
export class App {
constructor() {
this.nodes = ['Angular2', 'typescript', 'js']
}
getSubNodes( node: string ) {
if( node === 'Angular2') {
return ['2.0.0', '1.4.2']
}
if ( node === 'typescript' ) {
return ['1.7.3'];
}
if ( node === 'js' ) {
return ['es-6'];
}
}
}
'my-tree'是一个简单的组件 -
@Component({ selector: 'my-tree', providers: [], inputs: ['title'], template: `
- {{title}}
当执行这段代码时,控制台会记录以下错误 -
Expression 'getSubNodes(node) in App@2:15' has changed after it was checked. Previous value: '2.0.0,1.4.2'. Current value: '2.0.0,1.4.2'.
请参阅plunk以查看实际代码。
我的代码的思路是创建一棵树(仅为示例),第一层来自一个数组(硬编码的值),第二层来自一个函数,该函数根据第一层的当前节点(或值)返回下一组值。
而正是在调用这个函数的地方,Angular会抱怨表达式在检查后被改变了。尽管值在错误消息中报告的确实与之前完全相同。
我在SO上搜索了这个错误,并找到了一些参考资料,但大多数都建议调用变更检测。我无法理解为什么需要这样做,以及如何做。我还看到说这只是一个诊断消息,在生产模式下不会抛出。
在*ngFor中调用函数是不可能的吗?如何摆脱这个错误?
Angular2中嵌套*ngFor引发'Expression has changed after it was checked'问题的原因是每次Angular检查值时都会得到一个新的数组实例,因此它们永远不会相同。Angular只比较数组的实例相等性,而不比较值的相等性。这个检查只在开发模式下进行。
解决这个问题的方法是返回一个固定的数组实例,而不是每次都返回一个新的数组。可以将这些数组实例定义为类的属性,然后在函数中返回这些属性。
如果无法始终定义满足此条件的返回值,可以选择使用({changeDetection:ChangeDetectionStrategy.OnPush})并通知Angular进行更改。可以查看github.com/angular/angular/issues/4746或实现doCheck angular.io/docs/js/latest/api/core/DoCheck-interface.html。
总结起来,解决这个问题的方法有两种:一种是返回固定的数组实例,另一种是使用changeDetection或doCheck来通知Angular进行更改。
问题的原因是getSubNodes(node)
违反了幂等规则,因为每次调用该方法时都会返回一个新的数组。
Angular的模板表达式部分的开发指南中提到:
模板表达式可以使应用程序变得成功或失败。除非您有非常充分的理由在特定情况下打破这些规则,否则请遵循以下准则。
...
幂等表达式
在Angular术语中,幂等表达式在其依赖值发生变化之前总是返回完全相同的内容。
在JavaScript虚拟机的单个执行过程中,依赖值不应该发生变化。如果幂等表达式返回一个字符串或数字,那么当连续两次调用时,它将返回相同的字符串或数字。如果表达式返回一个对象(包括Date或Array),那么当连续两次调用时,它将返回相同的对象引用。
在开发模式下,如果违反了幂等性要求,Angular会发出警告,因为在开发模式下,模板绑定会进行两次检查,以找出这些类型的违规行为。Angular试图提供帮助,因为如果您正在做一些不同的事情,比如修改在父组件中可见的应用程序状态,那么由于在更改检测期间只对组件树进行一次遍历,所以父组件的视图将不会更新。也就是说,一旦检查了父组件的更改,即使后代组件更改了父组件在视图/模板中绑定的一些数据,父组件也不会再次被检查。
为了解决这个问题,需要重新设计代码,以遵循这个规则。