ExpressionChangedAfterItHasBeenCheckedError:通过迭代地图

Lio*_*-On 3 rxjs angular

我有一个简单的 Angular 组件,在渲染时会抛出以下错误:

MyComponent.html:10 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'ngForOf: [object Map Iterator]'. 
Current value: 'ngForOf: [object Map Iterator]'.
Run Code Online (Sandbox Code Playgroud)

错误会抛出两次,即使移动到没有其他代码的最低限度的 Angular 项目(只是一个 AppComponent 包装这个组件)。

我将代码最小化到仍然重现问题的最小形式。我用硬编码的 observable 替换了数据服务。

成分

MyComponent.html:10 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'ngForOf: [object Map Iterator]'. 
Current value: 'ngForOf: [object Map Iterator]'.
Run Code Online (Sandbox Code Playgroud)

请注意,asyncPipe在这种情况下使用更常见 - 但我不能使用它。

模板

@Component({
  selector: 'my-component',
  templateUrl: './my.component.html',
  styleUrls: ['./my.component.scss'],
})
export class MyComponent implements OnInit, OnDestroy {

  private cobs: Map<number, string>;
  private cobSub: Subscription;

  constructor() {}

  ngOnInit() {
    // originally data comes from a service...
    this.cobSub = of(new Map<number, string>([[1, 'a'], [2, 'b']]))
      .subscribe({
        next: cobs => { this.cobs = cobs; },
      });
  }

  ngOnDestroy(): void {
    this.cobSub.unsubscribe();
  }

}
Run Code Online (Sandbox Code Playgroud)

为什么会出现问题?

如何修复?

Fat*_*zli 7

您在 Map 上迭代的方式有问题,您可以尝试:

export class AppComponent implements OnInit {
  private cobs = new Map<number, string>();
  private cobSub: Subscription;

  constructor() {}

  ngOnInit() {
    this.cobSub = of(new Map<number, string>([[1, 'a'], [2, 'b']]))
      .subscribe( cobs => {
        this.cobs = cobs;
      });
  }

  getKeys(map){
    return Array.from(map.keys()); // add it
  }
  ngOnDestroy(): void {
    this.cobSub.unsubscribe();
  }
}
Run Code Online (Sandbox Code Playgroud)

在 html 中:

<div>
  <table *ngIf="cobs">
    <tr>
      <th>Id</th>
      <th>Class of Business</th>
    </tr>
    <tr *ngFor="let key of getKeys(cobs)">
      <td>{{key}}</td>
      <td>{{cobs.get(key)}}</td>
    </tr>
  </table>
</div>
Run Code Online (Sandbox Code Playgroud)

演示

如果您使用的是 Angular 6.1+ ,则可以使用默认管道键值

<tr *ngFor="let key of cobs | keyvalue">
          <td>{{key.key}}</td>
          <td>{{key.value}}</td>
        </tr>
Run Code Online (Sandbox Code Playgroud)

  • map.keys() 返回一个可迭代对象,但 getKeys(cobs) 返回它的数组。如果您更改为 ```getKeys(map){ console.log(map , map.keys(), Array.from(map.keys()) ) return Array.from(map.keys()); }``` 你可以看到map.keys() 是一个复杂的Map Iterator 对象,我认为Angular 无法以正确的方式检测变化。 (3认同)
  • 这是因为绑定到模板中的方法 ...keys() 。每次运行更改检测时都会调用它。object.keys() 每次被调用时都会返回一个新对象,Angular 会将其识别为意外更改,从而导致错误。 (2认同)