Подтвердить что ты не робот

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) Какие шаги я могу предпринять для решения этой проблемы?

4b9b3361

Ответ 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)" - это изменение, которое я буду излучать