Angular 4: `ExpressionChangedAfterItHasBeenCheckedError: выражение изменилось после его проверки
Каждый EventEmiiter в моем дочернем модуле дает эту ошибку, и я не могу найти исправление для этого.
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.
Это то, что запускает мой EventEmitter:
ngOnChanges(changes: any) {
if (changes.groupingTabValid) {
if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue) {
this.groupingTabValidChange.emit(this.groupingTabValid);
}
}
}
Вот мой "основной" компонент HTML
<year-overview-grouping [definitionDetails]="definitionDetails"
[fixedData]="fixedData"
[showValidation]="showValidation"
[groupingTabValid]="groupingTabValid"
(groupingTabValidChange)="setValidators('groupingTab', $event)">
</year-overview-grouping>
Что вызывает эту функцию
public setValidators(validator: string, e: boolean) {
switch (validator) {
case "groupingTab":
this.groupingTabValid = e;
break;
case "selectionTab":
this.selectionTabValid = e;
break;
}
if (this.groupingTabValid && this.selectionTabValid) {
this.valid = true;
} else {
this.valid = false;
}
}
1) В простом объяснении, что вызывает эту ошибку?
2) Какие шаги я могу предпринять для решения этой проблемы?
Ответы
Ответ 1
Следуйте правилу однонаправленного потока данных
Попробуйте отложить вызов для испускания для одного тика с помощью setTimeout
if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue) {
setTimeout(() => this.groupingTabValidChange.emit(this.groupingTabValid), 0)
}
Ответ 2
Вы можете избежать использования setTimeout
и сообщить Angular, что изменения произойдут после первоначальной проверки.
Вы можете ввести ChangeDetectorRef
в свой компонент и использовать его метод markForCheck
.
/* ... */
constructor(private cdr: ChangeDetectorRef) {}
/* ... */
if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue {
this.cdr.markForCheck();
this.groupingTabValidChange.emit(this.groupingTabValid);
}
Ответ 3
Я только начинаю смотреть на этот тип исключения сам, так что нет эксперта, но предположим, что он там, чтобы остановить контуры обратной связи.
Я не вижу никакой очевидной причины, по которой вы хотите отправить значение вниз ребенку и вернуться к родительскому, но предположите, что ребенок применяет дополнительную логику.
Для меня лучше использовать любую дополнительную логику в сервисе, а не в дочернем компоненте.
Я понимаю, что код плунжера не имеет цикла, но механизм Angular кажется довольно простым - просто переопределяет значения в конце цикла. Заметьте, groupingTabValid в вашем коде вопроса обратная связь напрямую.
Рассматривая код плункера как заданный, структурно изменение возраста может быть обработано родителем. Событие click было бы (click)=changeAge(age.value)
, а метод (для родителя) обработать его
changeAge(inputAge) {
this.newAge = inputAge;
this.person.age = inputAge;
}
Вот вилка плункера. Измененный плункер
Детский компонент все еще обновляет person.age, но больше не вызывает ошибку, так как значение такое же, как установлено для родителя.
Ответ 4
Просто импортируйте ChangeDetectionStrategy, а затем добавьте это в свой компонент
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-bla-bla-bla',
templateUrl: './xxxxx'
})
Ответ 5
У меня такая же проблема. Чтобы решить эту проблему, я сделал это littel hack.
setTimeout((()=>this.valueSelect.emit(idChange)), 0);
где "this.valueSelect.emit(idChange)" - это изменение, которое я буду излучать