Пожалуйста, объясните мне, почему я продолжаю получать эту ошибку: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Очевидно, я получаю это только в режиме разработки, этого не происходит в моей производственной сборке, но это очень раздражает, и я просто не понимаю преимуществ наличия ошибки в моей среде разработки, которая не отображается в продукте - - вероятно, из-за моего непонимания.
Обычно исправить это достаточно просто, я просто помещаю код, вызывающий ошибку, в setTimeout следующим образом:
setTimeout(()=> {
this.isLoading = true;
}, 0);
Или принудительно обнаружить изменения с помощью такого конструктора constructor(private cd: ChangeDetectorRef) {}
:
this.isLoading = true;
this.cd.detectChanges();
Но почему я постоянно сталкиваюсь с этой ошибкой? Я хочу понять это, чтобы избежать этих хакерских исправлений в будущем.
источник
Ответы:
У меня была аналогичная проблема. Глядя на документацию по хукам жизненного цикла , я переключился
ngAfterViewInit
на,ngAfterContentInit
и это сработало.источник
setTimeout
как показано выше, помогло. Благодарность!ngAfterContentChecked
работает здесь, покаngAfterContentInit
все еще выдает ошибку.Эта ошибка указывает на реальную проблему в вашем приложении, поэтому имеет смысл создать исключение.
При
devMode
обнаружении изменений после каждого обычного запуска обнаружения изменений добавляется дополнительный поворот, чтобы проверить, изменилась ли модель.Если модель изменилась между обычным и дополнительным ходом обнаружения изменений, это означает, что либо
которые оба плохи, потому что неясно, как действовать дальше, потому что модель может никогда не стабилизироваться.
Если Angular запускает обнаружение изменений до тех пор, пока модель не стабилизируется, он может работать вечно. Если Angular не запускает обнаружение изменений, представление может не отражать текущее состояние модели.
См. Также В чем разница между производственным режимом и режимом разработки в Angular2?
источник
ngOnInit
илиngOnChanges
для изменения модели (некоторые обратные вызовы жизненного цикла позволяют изменять модель, другие нет, я не помню точно, какие из них делают или нет). Не привязывайтесь к методам или функциям в представлении, вместо этого привязывайтесь к полям и обновляйте поля в обработчиках событий. Если вам необходимо выполнить привязку к методам, убедитесь, что они всегда возвращают один и тот же экземпляр значения, если на самом деле не было изменений. Обнаружение изменений будет часто вызывать эти методы.changeRef.detectChanges()
Доказательство тому, что вызов является решением / подавляет ошибку. Это похоже на изменение состояния внутри$scope.$watch()
в Angular 1.cdRef.detectChanges()
необходимо только в некоторых странных крайних случаях, и вам следует внимательно присмотреться, когда вам это нужно, чтобы правильно понять, почему.Большое понимание пришло, когда я понял крючки жизненного цикла Angular и их связь с обнаружением изменений.
Я пытался заставить Angular обновить глобальный флаг, привязанный к
*ngIf
элементу, и пытался изменить этот флаг внутриngOnInit()
ловушки жизненного цикла другого компонента.Согласно документации, этот метод вызывается после того, как Angular уже обнаружил изменения:
Таким образом, обновление флага внутри
ngOnChanges()
не инициирует обнаружение изменений. Затем, как только обнаружение изменений естественным образом сработает снова, значение флага изменится, и выдается ошибка.В моем случае я изменил это:
К этому:
и это устранило проблему :)
источник
router.navigate
) при загрузке к фрагменту, если он присутствует в URL-адресе. Этот код изначально был помещен в то место,AfterViewInit
где я получал ошибку, затем я переместился, как вы говорите, в конструктор, но он не соблюдал фрагмент. Переходим кngOnInit
решенному :) спасибо!setInterval()
работает также, если он должен срабатывать после другого кода события времени жизни.Обновить
Я настоятельно рекомендую в первую очередь начать с самоответа ОП : как следует подумайте о том, что можно сделать в
constructor
сравнении с тем, что следует делатьngOnChanges()
.Оригинал
Это скорее побочное примечание, чем ответ, но оно может кому-то помочь. Я наткнулся на эту проблему, когда пытался сделать наличие кнопки зависимой от состояния формы:
<button *ngIf="form.pristine">Yo</button>
Насколько мне известно, этот синтаксис приводит к тому, что кнопка добавляется и удаляется из DOM в зависимости от условия. Что, в свою очередь, приводит к
ExpressionChangedAfterItHasBeenCheckedError
.В моем случае исправление (хотя я не утверждаю, что я понимаю все последствия разницы) заключалось в использовании
display: none
вместо этого:<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>
источник
[hidden]
вместо очень многословной[style.display]
части. :)[hidden]
не всегда будет иметь такое же поведение, какdisplay: none
Angular запускает обнаружение изменений, и когда обнаруживает, что некоторые значения, которые были переданы дочернему компоненту, были изменены, Angular выдает следующую ошибку:
ExpressionChangedAfterItHasBeenCheckedError
нажмите, чтобы узнать большеЧтобы исправить это, мы можем использовать
AfterContentChecked
крючок жизненного цикла иисточник
Я использую ng2-carouselamos (Angular 8 и Bootstrap 4)
Эти шаги устранили мою проблему:
AfterViewChecked
constructor(private changeDetector : ChangeDetectorRef ) {}
ngAfterViewChecked(){ this.changeDetector.detectChanges(); }
источник
Были интересные ответы, но я, похоже, не нашел ни одного, соответствующего моим потребностям, ближайшим из которых был @ chittrang-mishra, который относится только к одной конкретной функции, а не к нескольким переключателям, как в моем приложении.
Я не хочу , чтобы использовать
[hidden]
в своих интересах*ngIf
даже не являясь частью DOM , так что я нашел следующее решение , которое не может быть лучше для всех , как он подавляет ошибку вместо того , чтобы исправить ее, но в моем случае , когда я знаю, окончательный результат правильный, для моего приложения он подходит.Я реализовал
AfterViewChecked
, добавил,constructor(private changeDetector : ChangeDetectorRef ) {}
а затемЯ надеюсь, что это поможет другим, как и многие другие помогли мне.
источник
Выполните следующие шаги:
1. Используйте ChangeDetectorRef, импортировав его из @ angular / core следующим образом:
2. Реализуйте его в constructor () следующим образом:
3. Добавьте следующий метод к вашей функции, которую вы вызываете при таком событии, как нажатие кнопки. Вот так это выглядит:
источник
В моем случае у меня была эта проблема в моем файле спецификации при выполнении моих тестов.
Мне пришлось сменить
ngIf
на[hidden]
<app-loading *ngIf="isLoading"></app-loading>
к
<app-loading [hidden]="!isLoading"></app-loading>
источник
[hidden]
: Talkingdotnet.com/dont-use-hidden-attribute-angularjs-2*ngIf
изменяется DOM, добавляется и удаляется элемент со страницы, при этом[hidden]
изменяется видимость элемента, не удаляя его из DOM.Я столкнулся с той же проблемой, что и значение в одном из массивов моего компонента. Но вместо того, чтобы обнаруживать изменения при изменении значения, я изменил стратегию обнаружения изменения компонента на
onPush
(которая будет обнаруживать изменения при изменении объекта, а не при изменении значения).источник
Ссылаясь на статью https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
Таким образом, механизм обнаружения изменений фактически работает таким образом, что и обнаружение изменений, и дайджесты проверки выполняются синхронно. Это означает, что если мы обновляем свойства асинхронно, значения не будут обновляться во время цикла проверки, и мы не получим
ExpressionChanged...
ошибку. Причина, по которой мы получаем эту ошибку, заключается в том, что во время процесса проверки Angular видит значения, отличные от тех, которые он записал на этапе обнаружения изменений. Чтобы этого избежать ....1) Используйте changeDetectorRef
2) используйте setTimeOut. Это выполнит ваш код в другой виртуальной машине как макро-задачу. Angular не увидит эти изменения во время процесса проверки, и вы не получите эту ошибку.
3) Если вы действительно хотите выполнить свой код на той же виртуальной машине, как
Это создаст микрозадачу. Очередь микрозадач обрабатывается после завершения выполнения текущего синхронного кода, поэтому обновление свойства произойдет после этапа проверки.
источник
@HostBinding
может быть непонятным источником этой ошибки.Например, допустим, у вас есть следующая привязка хоста в компоненте
Для простоты предположим, что это свойство обновляется через следующее свойство ввода:
В родительском компоненте вы программно устанавливаете его в
ngAfterViewInit
Вот что происходит:
carousel
(через ViewChild)carousel
покаngAfterViewInit()
(он будет нулевым)style_groupBG = 'red'
background: red
компонент ImageCarousel хостаcarousel.style.background
и недостаточно умен, чтобы знать, что это не проблема, поэтому он выдает исключение.Одно из решений - ввести другую оболочку div insider ImageCarousel и установить для нее цвет фона, но тогда вы не получите некоторых преимуществ использования
HostBinding
(например, разрешение родителю контролировать полные границы объекта).Лучшее решение в родительском компоненте - добавить detectChanges () после настройки config.
Это может выглядеть довольно очевидным, изложенным таким образом, и очень похоже на другие ответы, но есть небольшая разница.
Рассмотрим случай, когда вы добавляете не
@HostBinding
раньше, чем во время разработки. Внезапно вы получаете эту ошибку, и это кажется бессмысленным.источник
Вот мои мысли о том, что происходит. Я не читал документацию, но уверен, что это одна из причин появления ошибки.
При использовании * ngIf он физически изменяет DOM, добавляя или удаляя элемент каждый раз, когда изменяется условие. Поэтому, если условие изменится до того, как оно будет отображено в представлении (что очень возможно в мире Angular), выдается ошибка. См. Объяснение здесь между режимами разработки и производства.
При использовании
[hidden]
он не меняет физически,DOM
а просто скрываетelement
изображение, скорее всего,CSS
сзади. Элемент все еще присутствует в модели DOM, но не отображается в зависимости от значения условия. Поэтому при использовании ошибки возникать не будет[hidden]
.источник
isProcessing()
это делает АМУ вещь, вы должны использовать!isProcessing()
для[hidden]
hidden
не «использует CSS в задней части», это обычное свойство HTML. developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…Пробовал большинство решений, предложенных выше. Только это сработало для меня в этом сценарии. Я использовал * ngIf для переключения неопределенной прогрессивной полосы углового материала на основе вызовов API, и он бросал
ExpressionChangedAfterItHasBeenCheckedError
.В рассматриваемом компоненте:
Хитрость заключается в том, чтобы обойти обнаружение изменений углового компонента с помощью ngzone.
PS: Не уверен, что это элегантное решение, но использование ловушки жизненного цикла AfterContentChecked и AfterViewChecked обязательно вызовет проблемы с производительностью, поскольку ваше приложение становится больше, поскольку оно запускается много раз.
источник
Советы по отладке
Эта ошибка может сбивать с толку, и легко сделать неверное предположение о том, когда именно она возникает. Я считаю полезным добавить множество таких отладочных инструкций по всем затронутым компонентам в соответствующих местах. Это помогает понять поток.
В родительских операторах put (важна точная строка EXPRESSIONCHANGED), но в остальном это всего лишь примеры:
В обратных вызовах child / services / timer:
Если вы запустите
detectChanges
вручную, добавьте журналирование и для этого:Затем в отладчике Chrome просто отфильтруйте «EXPRESSIONCHANGES». Это точно покажет вам поток и порядок всего, что устанавливается, а также точно, в какой момент Angular выдает ошибку.
Вы также можете щелкнуть серые ссылки, чтобы установить точки останова.
Еще одна вещь, на которую следует обратить внимание, если у вас есть свойства с одинаковыми именами во всем приложении (например,
style.background
), убедитесь, что вы отлаживаете то, что вы думаете о себе, - установив для него значение неясного цвета.источник
В моем случае у меня было свойство async
LoadingService
с BehavioralSubjectisLoading
Использование модели [hidden] работает, но * ngIf не работает
источник
Решение, которое сработало для меня с использованием rxjs
import { startWith, tap, delay } from 'rxjs/operators'; // Data field used to populate on the html dataSource: any; .... ngAfterViewInit() { this.yourAsyncData. .pipe( startWith(null), delay(0), tap((res) => this.dataSource = res) ).subscribe(); }
источник
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
когда изменения значения запускаются при изменении DOMdelay
устраняет ошибку. Работает аналогичноsetTimeout
.У меня была такая ошибка в Ionic3 (который использует Angular 4 как часть своего технологического стека).
Для меня это было так:
<ion-icon [name]="getFavIconName()"></ion-icon>
Итак, я пытался условно изменить тип ионного значка с a
pin
на a в зависимости отremove-circle
режима, в котором работал экран.Думаю, мне придется
*ngIf
вместо этого добавить .источник
Моя проблема была очевидна, когда я добавил,
*ngIf
но это не было причиной. Ошибка была вызвана изменением модели в{{}}
тегах, а затем попыткой отобразить измененную модель в*ngIf
операторе позже. Вот пример:Чтобы решить эту проблему, я сменил
changeMyModelValue()
место звонка на более понятное место.В моей ситуации я хотел
changeMyModelValue()
вызывать каждый раз, когда дочерний компонент менял данные. Это потребовало, чтобы я создал и испустил событие в дочернем компоненте, чтобы родитель мог его обработать (путем вызоваchangeMyModelValue()
. См. Https://angular.io/guide/component-interaction#parent-listens-for-child-eventисточник
Что касается моей проблемы, я читал github - «ExpressionChangedAfterItHasBeenCheckedError при изменении значения компонента« не модель »в afterViewInit» и решил добавить ngModel
Это устранило мою проблему, надеюсь, это кому-то поможет.
источник
ngModel
. И не могли бы вы объяснить, почему это должно быть полезно?Надеюсь, это поможет кому-то, кто придет сюда: мы
ngOnInit
выполняем сервисные вызовы следующим образом и используем переменнуюdisplayMain
для управления монтированием элементов в DOM.component.ts
и component.html
источник
Я получил эту ошибку, потому что использовал переменную в component.html, которая не была объявлена в component.ts. Как только я удалил часть в HTML, эта ошибка исчезла.
источник
Я получил эту ошибку, потому что я отправлял действия redux в модальном режиме, а модальный режим не был открыт в то время. Я отправлял действия в тот момент, когда модальный компонент получал ввод. Поэтому я поместил туда setTimeout, чтобы убедиться, что модальное окно открыто, а затем действия совпадают.
источник
Всем, кто борется с этим. Вот способ правильно отладить эту ошибку: https://blog.angular-university.io/angular-debugging/
В моем случае я действительно избавился от этой ошибки с помощью этого [скрытого] хака вместо * ngIf ...
Но предоставленная мной ссылка позволила мне найти ВИНОВНЫХ * ngIf :)
Наслаждаться.
источник
hidden
вместоngIf
не является взломом и вообще не решает сути проблемы. Вы просто маскируете проблему.Решение ... службы и rxjs ... эмиттеры событий и привязка свойств используют rxjs .. вам лучше реализовать его самостоятельно, больше контроля, легче отлаживать. Помните, что эмиттеры событий используют rxjs. Просто создайте сервис и внутри наблюдаемого, пусть каждый компонент подписывается на наблюдателя и либо передает новое значение, либо значение cosume по мере необходимости.
источник
Какое-то время я боролся с этой проблемой, в первую очередь при выполнении тестов в своей среде разработки. Мне удалось решить проблему с помощью простого setTimeout для ответа на вызов службы!
источник