Angular2:嵌套 *ngFor 导致“检查后表达式已更改”
Posted
技术标签:
【中文标题】Angular2:嵌套 *ngFor 导致“检查后表达式已更改”【英文标题】:Angular2: Nested *ngFor resulting in 'Expression has changed after it was checked' 【发布时间】:2016-04-30 04:58:39 【问题描述】:我有一个 angular2 组件“my-tree”,我在我的父级“my-app”组件中使用它。 “我的应用”如下:
@Component(
selector: 'my-app',
providers: [],
template: `
<my-tree *ngFor="#node of nodes" [title]="node">
<my-tree *ngFor="#subNode of getSubNodes(node)" [title]="subNode">
</my-tree>
</my-tree>
`,
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: `
<ul>
<li><span>title</span></li>
<ng-content></ng-content>
</ul>
`,
directives: []
)
export class MyTree
private title: string;
执行此操作时,控制台会记录以下错误 - 表达式 'getSubNodes(node) in App@2:15' 在检查后已更改。以前的值:'2.0.0,1.4.2'。当前值:'2.0.0,1.4.2'。
查看plunk 获取实际代码。
我的代码中的想法是创建一棵树(只是一个例子),第一层来自一个数组(硬编码值),第二层来自一个函数,它返回给定当前节点的下一组(或值)从第一级。 并且它调用这个函数,其中角度抱怨表达式在检查后被更改。尽管报告的值与错误消息中的先前值完全相同。 我在 SO 上搜索了这个错误,发现很少有参考资料,但他们大多建议调用更改检测。我无法理解为什么需要这样做以及如何做到这一点。我还读到这只是一条诊断消息,不会在生产模式下抛出。
不能在 *ngFor 中调用函数吗?应该怎么做才能摆脱这个错误?
【问题讨论】:
【参考方案1】:问题是
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'];
这里每次 Angular 检查值时它都会获取一个新的数组实例,因此它们永远不会相同。 Angular 不会仅比较数组的值的实例相等性。
此检查也仅在开发模式下进行。 另见What is difference between production and development mode in Angular2?
这样 Angular 就应该满足了
angular2 = ['2.0.0', '1.4.2'];
typeScript = ['1.7.3'];
js = ['es-6'];
getSubNodes( node: string )
if( node === 'Angular2')
return this.angular2;
if ( node === 'typescript' )
return this.typeScript;
if ( node === 'js' )
return this.js;
【讨论】:
好的,现在我明白错误信息的原因了。但是可能并不总是可以定义始终满足此标准的返回值。函数调用可能并不总是以这种方式返回。那么基本上是不是意味着不应该使用函数? 您可以选择@Component(changeDetection:ChangeDetectionStrategy.OnPush)
并通知 Angular 更改。见github.com/angular/angular/issues/4746或实现doCheck
angular.io/docs/js/latest/api/core/DoCheck-interface.html
这真的很有帮助。【参考方案2】:
问题是getSubNodes(node)
违反了幂等规则,因为每次调用该方法时都会返回一个新数组。
来自模板语法开发指南的Template Expressions section:
模板表达式可以成就或破坏应用程序。请遵循这些准则,除非您有非常充分的理由在您完全理解的特定情况下打破它们。 ....
幂等表达式
在 Angular 术语中,幂等表达式总是返回完全相同的东西,直到它的依赖值之一发生变化。
在 javascript 虚拟机的单次运行期间,相关值不应更改。如果幂等表达式返回字符串或数字,则在连续调用两次时它返回相同的字符串或数字。 如果表达式返回一个对象(包括 Date 或 Array),则在连续调用两次时返回相同的对象引用。
在开发模式下,如果违反了幂等性要求,Angular 会抱怨,因为在开发模式下,模板绑定会被检查两次,以发现这些类型的违规行为。 Angular 试图提供帮助,因为如果您正在做一些不同的事情,例如修改在父组件中可见的应用程序状态,则由于在更改检测期间单次通过组件树,父组件的视图不会得到更新。即,一旦检查了父组件的更改,就不会再次检查它,即使子组件更改了父组件在其视图/模板中绑定的某些数据。
要解决问题,请重新编写代码以不违反规则。
【讨论】:
以上是关于Angular2:嵌套 *ngFor 导致“检查后表达式已更改”的主要内容,如果未能解决你的问题,请参考以下文章
Firebase列表的嵌套Angular2 ngFor指令[重复]
如何使用 angular2 ngFor 深度嵌套的 json 值
具有嵌套 Observable/FirebaseObjectObservable 的 Angular2 ngFor 在更新时闪烁一些数据