Hey everyone!
I'm currently trying to wrap a DateRange component inside of a custom component in my application so that I can add boilerplate configuration in a single place. Everything was going well until I tried to set the value from a parent component. When I attempt to set the value writeValue inside the component is called properly and sets the value HOWEVER the value is then immediately overwritten and a "ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'Thu Feb 02 2023 10:56:57 GMT-0600 (Central Standard Time)'. Current value: 'null'" error is written to the console. If I look at the stacktrace it is pointing to the start date input in the component template.
Is there something in the component lifecycle that could be causing this issue? I have verified that nothing else attempts to set the value of the component.
Thanks in advance for any help or insight.
component.html
<kendo-daterange>
<kendo-floatinglabel text="{{label}}">
<kendo-dateinput kendoDateRangeStartInput [min]="selectRange.startDate" [max]="selectRange.endDate" [(value)]="startDate" fillMode="outline"></kendo-dateinput>
</kendo-floatinglabel>
<kendo-floatinglabel text="">
<kendo-dateinput kendoDateRangeEndInput [min]="selectRange.startDate" [max]="selectRange.endDate" [(value)]="endDate" fillMode="outline"></kendo-dateinput>
</kendo-floatinglabel>
<kendo-daterange-popup (close)="onClose()"></kendo-daterange-popup>
</kendo-daterange>
component.ts
import {Component, forwardRef, Input, OnInit, ViewEncapsulation} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { TICKET_DATE_FORMAT, DATE_CONSTS } from 'core/constants';
import { DateRange } from 'core/models/date-range';
@Component({
selector: 'lm-date-range',
templateUrl: './lm-date-range.component.html',
styleUrls: ['./lm-date-range.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi:true,
useExisting: LMDateRangeComponent
}
]
})
export class LMDateRangeComponent implements ControlValueAccessor, OnInit {
protected selectRange: DateRange = {startDate: DATE_CONSTS.MIN_DATE, endDate: DATE_CONSTS.MAX_DATE};
protected dateFormat = TICKET_DATE_FORMAT;
onChange = (value: DateRange) => {};
onTouched = () => {};
touched = false;
disabled = false;
@Input() label: string;
startDate: Date = null;
endDate: Date = null;
value: DateRange = null;
constructor() {
var x = 5;
}
ngOnInit(): void {
var x = 5;
}
writeValue(selected: DateRange) {
this.onChange(selected);
if (selected) {
this.value = {
startDate: typeof selected.startDate === 'string' ? new Date(selected.startDate) : selected.startDate,
endDate: typeof selected.endDate === 'string' ? new Date(selected.endDate) : selected.endDate,
};
this.startDate = this.value.startDate;
this.endDate = this.value.endDate;
}
else {
this.value = null;
this.startDate = null;
this.endDate = null;
}
}
registerOnChange(onChange: any) {
this.onChange = onChange;
}
registerOnTouched(onTouched: any) {
this.onTouched = onTouched;
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
setDisabledState(disabled: boolean) {
this.disabled = disabled;
}
onClose() {
this.markAsTouched();
this.value = { startDate: this.startDate, endDate: this.endDate };
this.onChange(this.value);
}
}
parentcomponent.html (just the component definition)
<lm-date-range formControlName="issueDateRange" label="Issue Date/Timeframe" class="range-input"></lm-date-range>
parentcomponent.ts
(the ticket variable is passed as an input to this component)
ngOnInit() {
if (this.ticket) {
this.f.issueDateRange.setValue({startDate: this.ticket.startDate, endDate: this.ticket.endDate});
}
else {
this.f.issueDateRange.setValue({startDate: newDate(), endDate: newDate()})
}
}