Angular:在某些情况下激活复选框会引发ExpressionChangedAfterItHaHasBeenCheckedError

时间:2018-06-26 09:50:58

标签: angular

我目前正在实施需要条款和条件协议的角度项目。因此,我创建了一个300 px的可滚动div,其中从数据库中加载了内容。我们要强制用户在激活复选框之前阅读所有条款和条件。

在T&C繁重的情况下,它运行良好。当T&C点亮时,不会调用可滚动功能。因此,我想在加载结束时初始化复选框的禁用状态,但是会抛出ExpressionChangedAfterItHasBeenCheckedError

这是我的项目引起的轰动:https://stackblitz.com/edit/angular-b9p6rx

我尝试了许多解决方案:

  • *ngIf替换为[hidden]
  • 使用功能评估状态
  • 签入afterViewContentInit
  • div内容上使用二传手

我想避免setTimeout调用来评估状态。

您看到什么解决方案?

有没有更好的方法来实现此功能?

先谢谢了。

app.component.html

    <div class="content-box" *ngIf="isNotUpToDateCGU">
    <h1>Terms and conditions</h1>
    <span>
        <i>{{agreementHeader1}}</i>
    </span>
    <br>
    <div #cguContainer id="content" class="cgu-content" (scroll)="checkReading()" [innerHTML]="cguContent"></div>
    <div class="text-right">
        <span>I accept the terms and conditions</span>
        <p-checkbox [(ngModel)]="areCGUAccepted" binary="true" [disabled]="!hasContentBeenRead"></p-checkbox>
    </div>
    <div class="text-right cgu-buttons">
        <button type="button" class="btn btn-primary mx-2" >Annuler</button>
        <button type="submit" class="btn btn-primary mx-2" [disabled]="!areCGUAccepted">Valider</button>
    </div>
</div>

app.component.ts

export class AppComponent  {
  public agreementHeader1: string;
    public cguContent: string;
    public hasContentBeenRead: boolean = false;
    public areCGUAccepted: boolean = false;
    public isNotUpToDateCGU: boolean  = false;

    private cguContainerRef: ElementRef;
    @ViewChild('cguContainer') set controlElRef(elementRef: ElementRef) {
      if (elementRef) {
        this.cguContainerRef = elementRef;
        this.checkReading();
      }
    }

    constructor(
        private confService: ConfigurationService,
    ) { }

    public ngOnInit(): void {
      this.confService.getConfiguration('./terms.json')
            .subscribe(result => {

                this.agreementHeader1 = 'You have to read all terms and conditions to accept them';

                // !!!!!!! invert the two lines to test with big terms
                // this.cguContent = result.bigTerms;
                this.cguContent = result.littleTerms;

                this.isNotUpToDateCGU = true; // load template
            });
    }

    public checkReading(): void {

        let element = this.cguContainerRef.nativeElement;

        // height of the div in css => height: 300px
        if (element.scrollHeight - element.scrollTop < 301) {
            this.hasContentBeenRead = true;
        }
    }
}

1 个答案:

答案 0 :(得分:0)

仅当应用程序处于开发模式时才会发生这种情况。对于您而言,发生这种情况是因为在您设置this.isNotUpToDateCGU = true;之后,不会发生新一轮的更改检测,直到将来可能会触发它。因此,在设置了this.isNotUpToDateCGU = true;

之后,手动触发更改检测

首次导入ChangeDetectorRef

import { ChangeDetectorRef  } from '@angular/core';

然后按如下所示使用它

constructor(
    private confService: ConfigurationService,
    private changeDetectorRef: ChangeDetectorRef
) { }

public ngOnInit(): void {
  this.confService.getConfiguration('./terms.json')
        .subscribe(result => {

            this.agreementHeader1 = 'You have to read all terms and conditions to accept them';

            // !!!!!!! invert the two lines to test with big terms
            // this.cguContent = result.bigTerms;
            this.cguContent = result.littleTerms;

            this.isNotUpToDateCGU = true; // load template
            this.changeDetectorRef.detectChanges();

        });
}

Working Demo

希望这对您有帮助!