Angular2:嵌套* ngFor导致'表达式在被检查后发生了变化'

时间:2016-01-23 17:01:05

标签: angular

我有一个angular2组件'my-tree',我在我的父'my-app'组件中使用它。 '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;

}

执行此操作时,将记录控制台,并显示以下错误 - 检查后,表达式'app2:15中的getSubNodes(节点)已更改。上一个值:'2.0.0,1.4.2'。当前价值:'2.0.0,1.4.2'。

有关实际代码,请参阅此plunk

我的代码中的想法是创建一个树(只是一个例子),第一个级别来自一个数组(硬编码值),第二个级别来自一个函数,它返回给定当前节点的下一个集合(或价值)来自第一级。 并且它调用了这个函数,其中angular抱怨表达式被检查后被更改。虽然报告的值与错误消息中较早的值完全相同。 我在SO上搜索了这个错误,发现很少有引用,但大多数时候他们建议调用更改检测。我无法理解为什么这是必需的以及如何做到这一点。我还读到这只是一条诊断消息,它不会在生产模式中抛出。

是否无法在* ngFor内调用函数?应该怎么做才能摆脱这个错误?

2 个答案:

答案 0 :(得分:3)

问题是

 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;
    }    
  }  

答案 1 :(得分:1)

问题是getSubNodes(node)违反了idempontent规则,因为每次调用该方法时都会返回一个新数组。

来自模板语法开发指南的Template Expressions section

  

模板表达式可以创建或中断应用程序。请遵循这些指南,除非您有充分的理由在您完全理解的特定情况下打破它们。    
....

     

幂等表达式

     

在Angular术语中,幂等表达式总是返回完全相同的东西,直到其中一个相关值发生变化。

     

在JavaScript虚拟机的一次转弯中,相关值不应更改。如果幂等表达式返回字符串或数字,则在连续两次调用时返回相同的字符串或数字。 如果表达式返回一个对象(包括Date或Array),则在连续两次调用时返回相同的对象引用。

在开发模式下,如果违反幂等要求,Angular会抱怨,因为在开发模式下,模板绑定会被检查两次,以查找这些类型的违规。 Angular试图提供帮助,因为如果您正在做一些不同的事情,比如修改父组件中可见的应用程序状态,那么由于在更改检测期间单次通过组件树,该父组件的视图将不会更新。即,一旦检查了父组件的更改,即使后代组件更改了父组件在其视图/模板中绑定的某些数据,也不会再次检查它。

要解决此问题,请将代码重新修改为不违反规则。