【问题标题】:Angular: ExpressionChangedAfterItHasBeenCheckedError when trying to disable buttonAngular:尝试禁用按钮时出现 ExpressionChangedAfterItHasBeenCheckedError
【发布时间】:2020-09-02 09:23:15
【问题描述】:

我使用 mat-dialog 来编辑我的个人资料页面的详细信息。当我单击“编辑年龄”按钮并弹出对话框窗口时,我收到 ExpressionChangedAfterItHasBeenCheckedError


我决定将所有编辑对话框的样式提取到单个 edit.component 中:

edit.component.html

<div class="navigation-control">
  <mat-icon (click)="onCancelButtonClicked()"
            class="close-button">close</mat-icon>
</div>

<div class="content-main">
  <ng-content select=".content-main"></ng-content>
</div>

<div class="content-bot">
  <button mat-raised-button
          (click)="onCancelButtonClicked()">Cancel</button>

  <button mat-raised-button
          (click)="onActionButtonClicked()"
          [lnDisableButton]="actionButtonDisabled">{{actionButtonValue}}</button>
</div>

edit.component.ts

@Component({ selector: 'ln-edit', ... })
export class EditComponent {
  @Input() actionButtonValue: string;
  @Input() actionButtonDisabled: boolean;
  @Output() cancelButtonClicked = new EventEmitter<void>();
  @Output() actionButtonClicked = new EventEmitter<void>();

  onCancelButtonClicked() {
    this.cancelButtonClicked.emit();
  }

  onActionButtonClicked() {
    this.actionButtonClicked.emit();
  }
}


为了避免在尝试禁用按钮和控件时出现 ExpressionChangedAfterItHasBeenCheckedError,我使用了这个 sn-p。但这并没有解决这个问题。

disable-button.directive.ts

@Directive({ selector: '[lnDisableButton]' })
export class DisableButtonDirective {
  @Input('lnDisableButton') isDisabled = false;

  @HostBinding('attr.disabled')
  get disabled() { return this.isDisabled; }
}


以下是mat-dialog 窗口的内容。当我单击“编辑年龄”按钮时,它会被实例化。当我删除 [actionButtonDisabled]="actionButtonDisabled" 时,错误消失了,但显然我需要该行来使功能禁用按钮。

age-edit.component.html

<ln-edit [actionButtonValue]="actionButtonValue"
         [actionButtonDisabled]="actionButtonDisabled"
         (cancelButtonClicked)="onCancelButtonClicked()"
         (actionButtonClicked)="onActionButtonClicked()">
  <form [formGroup]="ageForm"
        class="content-main">
    <ln-datepicker formControlName="birthday"
                   [appearance]="'standard'"
                   [label]="'Birthday'"
                   class="form-field">
    </ln-datepicker>
  </form>
</ln-edit>


我在 mat-dialog 弹出窗口的“ts”部分处理禁用/启用按钮。

age-edit.component.ts

@Component({ selector: 'ln-age-edit', ... })
export class AgeEditComponent implements OnInit, OnDestroy {
  ageForm: FormGroup;
  private initialFormValue: any;
  actionButtonDisabled = true;
  private unsubscribe = new Subject<void>();

  constructor(
    private editPhotoDialogRef: MatDialogRef<AgeEditComponent>,
    private fb: FormBuilder,
    @Inject(MAT_DIALOG_DATA) public dialogData: Date) { }

  ngOnInit() {
    this.initializeAgeForm();
    this.loadDataToAgeForm(this.dialogData);
    this.trackFormDistinct();
  }

  private initializeAgeForm(): void {
    this.ageForm = this.fb.group({
      birthday: null,
    });
  }

  loadDataToAgeForm(birthday: Date | null): void {
    if (!birthday) { return; }

    this.ageForm.setValue({ birthday });
    this.initialFormValue = this.ageForm.value;
  }

  get birthdayAC() { return this.ageForm.get('birthday') as AbstractControl; }

  get actionButtonValue(): string {
    return this.birthdayAC.value ? 'Update age' : 'Add age';
  }

  onCancelButtonClicked(): void {
    this.editPhotoDialogRef.close();
  }

  onActionButtonClicked(): void {
    this.editPhotoDialogRef.close({ ... });
  }

  trackFormDistinct(): void {
    this.ageForm.valueChanges.pipe(
      distinctUntilChanged(), // TODO: needed?
      takeUntil(this.unsubscribe)
    ).subscribe(val => {

      (this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
        || this.birthdayAC.value === null)
        ? this.actionButtonDisabled = true
        : this.actionButtonDisabled = false;
    });
  }

  ngOnDestroy() { ... }
}


我怀疑这与内容投影有关,但我不确定。 (......或者也许我的自定义'ln-datepicker'?)
有任何想法吗?
谢谢。

【问题讨论】:

  • 能否也添加积分错误信息?
  • @AndreiGătej - 你的意思是整个错误信息吗?还是别的什么?
  • 对不起,我只想说相关部分:从X更改为Y
  • 你是这个意思吗? ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。以前的值:“真”。当前值:“假”。在 throwErrorIfNoChangesMode (core.js:8092) at bindingUpdated (core.js:19773) at ɵɵproperty (core.js:20921) at AgeEditComponent_Template (age-edit.component.html:2) at executeTemplate (core.js:11949) at refreshView (core.js:11796) 在 refreshComponent (core.js:13229) 在 refreshChildComponents (core.js:11527) 在 refreshView (core.js:11848) 在 refreshDynamicEmbeddedViews (core.js:13154)

标签: html angular typescript


【解决方案1】:

据我所知,问题在于trackFormDistinct() 方法:

trackFormDistinct(): void {
  this.ageForm.valueChanges.pipe(
    distinctUntilChanged(), // TODO: needed?
    takeUntil(this.unsubscribe)
  ).subscribe(val => {

    (this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
      || this.birthdayAC.value === null)
      ? this.actionButtonDisabled = true
      : this.actionButtonDisabled = false;
  });
}

看起来因为this.ageForm.valueChanges,在 2 个更改检测周期中会有不同的值。我认为this.ageForm.valueChanges 发出是由于&lt;ln-datepicker&gt;

在一棵表单控件的树中,如果一个节点调用setValue,它的所有祖先都必须更新。我在this article 中写了更多关于Angular Forms 工作原理的文章。

我正在考虑两种选择:

  • 跳过ageForm的第一次发射,因为它表示表单控件树的初始化,所以这与subscribe的回调内部的逻辑无关。
this.ageForm.valueChanges.pipe(
  skip(1),
  distinctUntilChanged(), // TODO: needed?
  takeUntil(this.unsubscribe)
).subscribe(/* .... */)
  • false初始化actionButtonDisabled,因为错误提示它从true切换到false
actionButtonDisabled = false;

【讨论】:

    猜你喜欢
    • 2016-04-14
    • 1970-01-01
    • 2021-12-06
    • 2018-07-31
    • 1970-01-01
    • 2019-10-10
    • 2021-04-16
    • 2018-11-07
    • 1970-01-01
    相关资源
    最近更新 更多