首页 > 解决方案 > 使用 ChildView 时如何修复 ExpressionChangedAfterItHasBeenCheckedError?

问题描述

我有控制可能或可能在视图中的兄弟组件的指令(它是 *ngIfed)。我正在使用@ViewChild. 当*ngIf分辨率改变时ExpressionChangedAfterItHasBeenCheckedError抛出。如果我正确理解它的性质,这意味着以任何方式与视图绑定的某些组件字段在更改检测周期(字面意思是在 ngOnChnges 之后)确实改变了它的状态。这对我来说很清楚,但@ViewChild在变化检测阶段(afterViewChecked 我相信)本质上会改变绑定。

问题是,如何解决这个问题?我已经看到手动调用或类似ChangeDetectorRef#detechChanges()的建议afterViewChecked,但这对我来说感觉不对,乍一看应该会导致无限循环。

我有一个小演示 - 只需单击按钮https://stackblitz.com/edit/angular-9thvtm?file=src%2Fapp%2Fapp.component.html

模板

<button (click)="state=!state">toggle state</button>

<div [myDir]="stateDiv" #d="myDir">{{d.hasTestElement}}</div>


<div *ngIf="state" #stateDivMarker>
    Showing state div yessss
</div>

代码

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  name = "Angular";
  state = false;

  @ViewChild("stateDivMarker")
  stateDiv;
}

@Directive({
  selector: "[myDir]",
  exportAs: "myDir"
})
export class MyDir {
  @Input("myDir")
  testElement;

  get hasTestElement() {
    return !!this.testElement;
  }
}

标签: angular

解决方案


您必须确保 setter 没有设置未定义的值,因为您使用的是 *ngIf

private stateDiv: ElementRef;

@ViewChild('stateDivMarker') set content(content: ElementRef) {
    if(content) {
      this.stateDiv = content;
    }
 }

编辑

作为替代方案,我建议使用变化检测器

https://stackblitz.com/edit/angular-tfzp87?file=src/app/app.component.html

组件.ts

constructor(private changeDetector: ChangeDetectorRef) {}

toggleState() {
    this.state = !this.state;
    this.changeDetector.detectChanges();
}

组件.html

<hello name="{{ name }}"></hello>
<p>
    Start editing to see some magic happen :)
</p>

<button (click)="toggleState()">toggle state</button>

<div [myDir]="stateDiv" #d="myDir">{{d.hasTestElement}}</div>


<div *ngIf="state" #stateDivMarker>
    Showing state div yessss
</div>

推荐阅读