[英]Angular: ExpressionChangedAfterItHasBeenCheckedError when trying to disable button
I use mat-dialog to edit details of my profile page.我使用 mat-dialog 来编辑个人资料页面的详细信息。 I'm getting an ExpressionChangedAfterItHasBeenCheckedError when I click the 'Edit age' button and the dialog window pops up.
当我单击“编辑年龄”按钮并弹出对话框 window 时,我收到ExpressionChangedAfterItHasBeenCheckedError 。
I decided to extract the styling of all edit dialogs into a single edit.component:我决定将所有编辑对话框的样式提取到单个 edit.component 中:
edit.component.html编辑.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.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();
}
}
To avoid the ExpressionChangedAfterItHasBeenCheckedError when trying to disable buttons and controls, I used this snippet.为避免在尝试禁用按钮和控件时出现 ExpressionChangedAfterItHasBeenCheckedError,我使用了此代码段。 But that didn't solve this issue.
但这并没有解决这个问题。
disable-button.directive.ts禁用按钮.directive.ts
@Directive({ selector: '[lnDisableButton]' })
export class DisableButtonDirective {
@Input('lnDisableButton') isDisabled = false;
@HostBinding('attr.disabled')
get disabled() { return this.isDisabled; }
}
The following is the contents of a mat-dialog window. This gets instantiated when I click the 'Edit age' button.以下是 mat-dialog window 的内容。当我单击“编辑年龄”按钮时,它会被实例化。 When I remove the [actionButtonDisabled]="actionButtonDisabled", the error goes away, but obivously I need that line to make the functionality disable the button.
当我删除 [actionButtonDisabled]="actionButtonDisabled" 时,错误消失了,但显然我需要该行来使功能禁用按钮。
age-edit.component.html 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>
I handle the disabling/enabling the button in the 'ts' part of the mat-dialog popup.我在 mat-dialog 弹出窗口的“ts”部分处理禁用/启用按钮。
age-edit.component.ts年龄编辑.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() { ... }
}
I suspect this has something to do with content projection, but I'm not sure.我怀疑这与内容投影有关,但我不确定。 (...or perhaps with my custom 'ln-datepicker'?)
(...或者可能是我的自定义“ln-datepicker”?)
Any ideas?有任何想法吗?
Thanks.谢谢。
From what I can tell, the problem resides in trackFormDistinct()
method:据我所知,问题出在
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;
});
}
Looks like because of this.ageForm.valueChanges
, will have different values in the 2 change detection cycles.看起来因为
this.ageForm.valueChanges
,在 2 个变化检测周期中会有不同的值。 I think this.ageForm.valueChanges
emits due to <ln-datepicker>
.我认为
this.ageForm.valueChanges
由于<ln-datepicker>
发出的。
In a tree of form controls , if one node calls setValue
, all its ancestors will have to be updated.在表单控件树中,如果一个节点调用
setValue
,则必须更新其所有祖先。 I've written more about how Angular Forms work in this article .我在本文中详细介绍了 Angular Forms 的工作原理。
I'm thinking of 2 alternatives:我正在考虑两种选择:
ageForm
since it indicates the initialization of the form control tree, so this is irrelevant to the logic inside subscribe
's callback.ageForm
的第一次发射,因为它指示表单控件树的初始化,所以这与subscribe
的回调中的逻辑无关。this.ageForm.valueChanges.pipe(
skip(1),
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(/* .... */)
actionButtonDisabled
with false
, since the error complains that it switched from true
to false
false
初始化actionButtonDisabled
,因为错误抱怨它从true
切换到false
actionButtonDisabled = false;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.