Mat-Datepicker自定义组件ExpressionChangedAfterItHasBeenChecked报错
Mat-Datepicker custom component ExpressionChangedAfterItHasBeenChecked Error
我正在制作一个自定义日期选择器组件,因此它可以与本地化和 moment.js 以及响应式表单一起使用。
我已经设置了组件,但是在 DateChange 事件发生后,在退出区域后,它会抛出错误:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'.
此刻我不知道如何修复它,尽管我尝试用 ChangeDetectorRef
修复它,这使得我的验证无法触发,类似的事情发生在 this.control.updateValueAndValidty()
.
毕竟,它发生的时间比我触发任何事情晚了很多。我想这是输入元素的问题,所以我会尝试同时捕获它的更改事件。
所以这是 date-picker.component.ts
:
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { Component, OnInit, forwardRef, Input, Self, Optional, ChangeDetectorRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, Validator, NG_VALIDATORS, FormControl, NgControl, FormControlName } from '@angular/forms';
import * as _moment from 'moment';
import { MatDatepickerInputEvent, MAT_DATE_FORMATS, DateAdapter, MAT_DATE_LOCALE } from '@angular/material';
import { default as _rollupMoment } from 'moment';
const moment = _rollupMoment || _moment;
export const DATE_FORMATS = {
parse: {
dateInput: 'DD.MM.YYYY.',
},
display: {
dateInput: 'DD.MM.YYYY.',
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'MMMM YYYY',
},
};
@Component({
selector: 'app-date-picker',
templateUrl: './date-picker.component.html',
styleUrls: ['./date-picker.component.scss'],
providers: [
{ provide: MAT_DATE_LOCALE, useValue: 'sr' },
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
{ provide: MAT_DATE_FORMATS, useValue: DATE_FORMATS }
]
})
export class DatePickerComponent implements ControlValueAccessor {
control: FormControl;
private onChange = (value: any) => { };
private onTouched = (value: any) => { };
@Input() dateValue: string = null;
@Input() errorMessage: string = "This is required.";
@Input() public placeholder: string = null;
@Input() public minDate: string = null;
@Input() public maxDate: string = null;
@Input() public startDate: string = null;
@Input() public label: string = null;
constructor(@Optional() @Self() private ngControl: NgControl, @Optional() private controlName: FormControlName, private cdr: ChangeDetectorRef) {
if(this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
}
ngOnInit() {
moment.locale('sr');
this.control = this.controlName.control;
}
onDateChange(event: MatDatepickerInputEvent<Date>) {
this.dateValue = moment(event.value).format();
this.onChange(event.value);
}
// get errorState() {
// debugger;
// if(!this.dateValue){
// return this.ngControl.errors !== null && !!this.ngControl.touched;
// }
// return false;
// }
// ControlValueAccessor methods
writeValue(value: any): void {
if(value !== undefined || value !== '') {
this.dateValue = moment(value).format();
}
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched() {}
// registerOnTouched(fn) {
// this.onTouched = fn;
// }
}
这是date-picker.component.html
:
<div class="date-picker">
<mat-form-field class="full-width">
<mat-label>{{ label }}</mat-label>
<input #dpInput matInput
[matDatepicker]="componentDatePicker"
(dateChange)="onDateChange($event)"
[formControl]="control"
[value]="dateValue"
[min]="minDate"
[max]="maxDate"
[placeholder]="placeholder"
[formControl]='control'>
<mat-datepicker-toggle matSuffix [for]="componentDatePicker"></mat-datepicker-toggle>
<mat-datepicker #componentDatePicker></mat-datepicker>
<mat-error [innerHTML]="errorMessage"></mat-error>
</mat-form-field>
</div>
我正在通过返回具有值或 null
的 new Date(year, month, day)
对象来更改父组件中的 min
和 max
值。同一个选择器有多个实例,所以我发送字符串值来区分它们。
<app-date-picker
[minDate]="getMinDate('datepickerIdentifier')"
[maxDate]="getMaxDate('datepickerIdentifier')"
[placeholder]="'Placeholder string'"
[label]="'Label string'"
[errorMessage]="'Error string'"
formControlName="applicationEnd"
required>
</app-date-picker>
我正在尝试构建可重用的日期选择器组件,该组件将具有本地化、特定日期格式,并且可以轻松地合并到反应式表单中,因此我可以使用其余表单数据对其进行验证,并显示 Validation.required
错误。
不幸的是,我不知道从哪里开始调试它。
编辑:我试图制作一个 stackblitz,但它给了我很多错误,但仍然在这里:https://stackblitz.com/edit/angular-aylptw
onDateChange(event: MatDatepickerInputEvent<Date>) {
this.dateValue = moment(event.value).format();
this.onChange(event.value); <= THIS WAS THE PROBLEM
}
看起来,删除 this.onChange(event.value)
后,验证和组件都开始正常工作。
编辑:它已经消除了我遇到的其他问题,但仍然抛出 ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'.
我做到了
当 HTML 中的表达式在 Angular 已检查或比较 oldValue !== newValue
后发生更改时,将抛出此 ExpressionChangedAfterItHasBeenChecked
。按照以下方式将更改检测策略设置为 onPush
并触发手动更改检测,就像在最后一个代码片段中解释的那样。
@Component({
...
changeDetection: ChangeDetectionStrategy.OnPush
})
您正在关注 Angular material 文档,很好。
import { MatDateFormats } from '@angular/material/core'; //import MatDateFormats
export const APP_DATE_FORMATS: MatDateFormats = {
parse: {
dateInput: 'MM/DD/YYYY', //whichever format you are tyring, define here
},
display: {
dateInput: 'MM/DD/YYYY', //change accordingly, to display
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'MMMM YYYY',
}
};
请尝试在不同的文件中创建以上代码,这样更容易维护。
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
import { APP_DATE_FORMATS } from '../../helpers/datepicker.format';
import { MomentDateAdapter, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';
//register the imports respectively which are going to be addressed in providers
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter },
{
provide: MAT_DATE_FORMATS, useValue: APP_DATE_FORMATS,
deps: [
MAT_DATE_LOCALE,
MAT_MOMENT_DATE_ADAPTER_OPTIONS
]
}
]
如上所述更新您各自的模块提供程序并进行反应式表单验证,如果您遇到 Error: ExpressionChangedAfterItHasBeenChecked
然后从 `angular/core 定义以下 ChangeDetectorRef
以在各自的组件中解决。谢谢。
constructor(private _cdr: ChangeDetectorRef) {}
insideYourValidations() {
setTimeout(() => {
this._cdr.detectChanges() //call to update/detect your changes
}, 1500);
}
有时当我们得到 ExpressionchangeError
时,日期选择器可能不会更新日期值,使用 ChangeDetectorRef
如果用户尝试在不使用日期选择器的情况下手动输入,表单输入值会更新。
这个 me, fix my problem. zmag谢谢
我正在制作一个自定义日期选择器组件,因此它可以与本地化和 moment.js 以及响应式表单一起使用。
我已经设置了组件,但是在 DateChange 事件发生后,在退出区域后,它会抛出错误:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'.
此刻我不知道如何修复它,尽管我尝试用 ChangeDetectorRef
修复它,这使得我的验证无法触发,类似的事情发生在 this.control.updateValueAndValidty()
.
毕竟,它发生的时间比我触发任何事情晚了很多。我想这是输入元素的问题,所以我会尝试同时捕获它的更改事件。
所以这是 date-picker.component.ts
:
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { Component, OnInit, forwardRef, Input, Self, Optional, ChangeDetectorRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, Validator, NG_VALIDATORS, FormControl, NgControl, FormControlName } from '@angular/forms';
import * as _moment from 'moment';
import { MatDatepickerInputEvent, MAT_DATE_FORMATS, DateAdapter, MAT_DATE_LOCALE } from '@angular/material';
import { default as _rollupMoment } from 'moment';
const moment = _rollupMoment || _moment;
export const DATE_FORMATS = {
parse: {
dateInput: 'DD.MM.YYYY.',
},
display: {
dateInput: 'DD.MM.YYYY.',
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'MMMM YYYY',
},
};
@Component({
selector: 'app-date-picker',
templateUrl: './date-picker.component.html',
styleUrls: ['./date-picker.component.scss'],
providers: [
{ provide: MAT_DATE_LOCALE, useValue: 'sr' },
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
{ provide: MAT_DATE_FORMATS, useValue: DATE_FORMATS }
]
})
export class DatePickerComponent implements ControlValueAccessor {
control: FormControl;
private onChange = (value: any) => { };
private onTouched = (value: any) => { };
@Input() dateValue: string = null;
@Input() errorMessage: string = "This is required.";
@Input() public placeholder: string = null;
@Input() public minDate: string = null;
@Input() public maxDate: string = null;
@Input() public startDate: string = null;
@Input() public label: string = null;
constructor(@Optional() @Self() private ngControl: NgControl, @Optional() private controlName: FormControlName, private cdr: ChangeDetectorRef) {
if(this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
}
ngOnInit() {
moment.locale('sr');
this.control = this.controlName.control;
}
onDateChange(event: MatDatepickerInputEvent<Date>) {
this.dateValue = moment(event.value).format();
this.onChange(event.value);
}
// get errorState() {
// debugger;
// if(!this.dateValue){
// return this.ngControl.errors !== null && !!this.ngControl.touched;
// }
// return false;
// }
// ControlValueAccessor methods
writeValue(value: any): void {
if(value !== undefined || value !== '') {
this.dateValue = moment(value).format();
}
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched() {}
// registerOnTouched(fn) {
// this.onTouched = fn;
// }
}
这是date-picker.component.html
:
<div class="date-picker">
<mat-form-field class="full-width">
<mat-label>{{ label }}</mat-label>
<input #dpInput matInput
[matDatepicker]="componentDatePicker"
(dateChange)="onDateChange($event)"
[formControl]="control"
[value]="dateValue"
[min]="minDate"
[max]="maxDate"
[placeholder]="placeholder"
[formControl]='control'>
<mat-datepicker-toggle matSuffix [for]="componentDatePicker"></mat-datepicker-toggle>
<mat-datepicker #componentDatePicker></mat-datepicker>
<mat-error [innerHTML]="errorMessage"></mat-error>
</mat-form-field>
</div>
我正在通过返回具有值或 null
的 new Date(year, month, day)
对象来更改父组件中的 min
和 max
值。同一个选择器有多个实例,所以我发送字符串值来区分它们。
<app-date-picker
[minDate]="getMinDate('datepickerIdentifier')"
[maxDate]="getMaxDate('datepickerIdentifier')"
[placeholder]="'Placeholder string'"
[label]="'Label string'"
[errorMessage]="'Error string'"
formControlName="applicationEnd"
required>
</app-date-picker>
我正在尝试构建可重用的日期选择器组件,该组件将具有本地化、特定日期格式,并且可以轻松地合并到反应式表单中,因此我可以使用其余表单数据对其进行验证,并显示 Validation.required
错误。
不幸的是,我不知道从哪里开始调试它。
编辑:我试图制作一个 stackblitz,但它给了我很多错误,但仍然在这里:https://stackblitz.com/edit/angular-aylptw
onDateChange(event: MatDatepickerInputEvent<Date>) {
this.dateValue = moment(event.value).format();
this.onChange(event.value); <= THIS WAS THE PROBLEM
}
看起来,删除 this.onChange(event.value)
后,验证和组件都开始正常工作。
编辑:它已经消除了我遇到的其他问题,但仍然抛出 ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'.
我做到了
当 HTML 中的表达式在 Angular 已检查或比较 oldValue !== newValue
后发生更改时,将抛出此 ExpressionChangedAfterItHasBeenChecked
。按照以下方式将更改检测策略设置为 onPush
并触发手动更改检测,就像在最后一个代码片段中解释的那样。
@Component({
...
changeDetection: ChangeDetectionStrategy.OnPush
})
您正在关注 Angular material 文档,很好。
import { MatDateFormats } from '@angular/material/core'; //import MatDateFormats
export const APP_DATE_FORMATS: MatDateFormats = {
parse: {
dateInput: 'MM/DD/YYYY', //whichever format you are tyring, define here
},
display: {
dateInput: 'MM/DD/YYYY', //change accordingly, to display
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'MMMM YYYY',
}
};
请尝试在不同的文件中创建以上代码,这样更容易维护。
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
import { APP_DATE_FORMATS } from '../../helpers/datepicker.format';
import { MomentDateAdapter, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter';
//register the imports respectively which are going to be addressed in providers
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter },
{
provide: MAT_DATE_FORMATS, useValue: APP_DATE_FORMATS,
deps: [
MAT_DATE_LOCALE,
MAT_MOMENT_DATE_ADAPTER_OPTIONS
]
}
]
如上所述更新您各自的模块提供程序并进行反应式表单验证,如果您遇到 Error: ExpressionChangedAfterItHasBeenChecked
然后从 `angular/core 定义以下 ChangeDetectorRef
以在各自的组件中解决。谢谢。
constructor(private _cdr: ChangeDetectorRef) {}
insideYourValidations() {
setTimeout(() => {
this._cdr.detectChanges() //call to update/detect your changes
}, 1500);
}
有时当我们得到 ExpressionchangeError
时,日期选择器可能不会更新日期值,使用 ChangeDetectorRef
如果用户尝试在不使用日期选择器的情况下手动输入,表单输入值会更新。
这个