Почему компонент в этом простом панке
@Component({
selector: 'my-app',
template: `<div>I'm {{message}} </div>`,
})
export class App {
message:string = 'loading :(';
ngAfterViewInit() {
this.updateMessage();
}
updateMessage(){
this.message = 'all done loading :)'
}
}
метание:
ИСКЛЮЧЕНИЕ: выражение «Я {{message}} в приложении @ 0: 5» изменилось после того, как оно было проверено. Предыдущее значение: «Я загружаю :(». Текущее значение: «Я все загрузил :)» в [I'm {{message}} в приложении @ 0: 5]
когда все, что я делаю, это обновление простой привязки, когда мое представление инициируется?
typescript
angular
Дрю Мур
источник
источник
ExpressionChangedAfterItHasBeenCheckedError
ошибке, подробно объясняет поведение.ChangeDetectionStrategy
при использованииdetectChanges()
stackoverflow.com/questions/39787038/…Ответы:
Как утверждает drewmoore, правильным решением в этом случае является ручное инициирование обнаружения изменений для текущего компонента. Это делается с помощью
detectChanges()
методаChangeDetectorRef
объекта (импортированного изangular2/core
) или егоmarkForCheck()
метода, который также обновляет все родительские компоненты. Соответствующий пример :Вот также Плункеры, демонстрирующие подходы ngOnInit , setTimeout и enableProdMode на всякий случай.
источник
cdr : any
или что-то подобное подmessage
декларацией. Просто беспокоишься, что я что-то пропустил?Во-первых, обратите внимание, что это исключение будет сгенерировано только при запуске приложения в режиме разработки (что имеет место по умолчанию в бета-0): если вы вызываете
enableProdMode()
при загрузке приложения, оно не будет выброшено ( см. обновленный план ).Во-вторых, не делайте этого, потому что это исключение вызывается по уважительной причине: короче говоря, когда в режиме разработки, каждый раунд обнаружения изменений сопровождается сразу же вторым раундом, который проверяет, что с конца первого не изменились привязки, поскольку это указывает на то, что изменения вызваны самим обнаружением изменений.
В вашем соединении привязка
{{message}}
изменяется вашим вызовомsetMessage()
, что происходит вngAfterViewInit
ловушке, которая происходит как часть начального хода обнаружения изменений. Это само по себе не является проблемой - проблема в том, чтоsetMessage()
изменяет привязку, но не запускает новый раунд обнаружения изменений, а это означает, что это изменение не будет обнаружено, пока какой-то будущий раунд обнаружения изменений не будет запущен где-то еще.Вывод: все, что изменяет привязку, должно инициировать раунд обнаружения изменений, когда это происходит.
Обновите в ответ на все запросы пример того, как это сделать : решение @ Tycho работает, как и три метода в ответе @MarkRajcok. Но, честно говоря, все они мне кажутся уродливыми и неправильными, вроде тех хаков, к которым мы привыкли опираться в ng1.
Конечно, есть случайные обстоятельства , при которых эти писаки подходящие, но если вы используете их на что - нибудь более чем очень эпизодически, это признак того, что вы сражаетесь рамки , а не полностью охватывает его реактивную природу.
ИМХО, более идиоматический, "Angular2 способ" подхода к этому - что-то вроде: ( plunk )
источник
detectChanges()
.updateMessage
, а неsetMessage
ngAfterViewChecked()
работал на меня:источник
Я исправил это, добавив ChangeDetectionStrategy из углового ядра.
источник
CheckOnce
( документация )this.cdr.detectChanges();
за тем, что вы пытаетесь загрузить. Потому что это может быть потому, что обнаружение изменений не срабатываетВы не можете использовать,
ngOnInit
потому что вы просто меняете переменную-членmessage
?Если вы хотите получить доступ к ссылке на дочерний компонент
@ViewChild(ChildComponent)
, вам действительно нужно ждать ееngAfterViewInit
.Грязное исправление - вызвать
updateMessage()
в следующем цикле событий, например, setTimeout.источник
Для этого я попробовал выше ответы, многие из которых не работает в последней версии Angular (6 или более поздняя версия)
Я использую Контроль материала, который требует изменений после первого связывания.
Добавление моего ответа так, это помогает некоторым решить конкретную проблему.
источник
В статье все, что нужно знать об
ExpressionChangedAfterItHasBeenCheckedError
ошибке подробно объясняет поведение.Проблема с вашей настройкой заключается в том, что
ngAfterViewInit
ловушка жизненного цикла выполняется после обнаружения изменений, обрабатываемых обновлениями DOM. И вы эффективно меняете свойство, используемое в шаблоне в этом хуке, что означает, что DOM необходимо перерисовать:и это потребует еще одного цикла обнаружения изменений, а Angular по конструкции выполняет только один цикл дайджеста.
У вас есть два варианта, как это исправить:
обновлять свойство асинхронно либо используя
setTimeout
,Promise.then
либо асинхронно наблюдаемую, указанную в шаблоневыполнить обновление свойства в хуке перед обновлением DOM - ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.
источник
Вам просто нужно обновить ваше сообщение в правильном хуке жизненного цикла, в этом случае
ngAfterContentChecked
вместоngAfterViewInit
, потому что в ngAfterViewInit проверка переменной сообщения была начата, но еще не закончена.см .: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview
поэтому код будет просто:
посмотрите рабочую демонстрацию на Plunker.
источник
@ViewChildren()
счетчика, основанного на длине, который был заполнен Observable. Это было единственное решение, которое сработало для меня!ngAfterContentChecked
иChangeDetectorRef
сверху сработало для меня. НаngAfterContentChecked
звонил -this.cdr.detectChanges();
Эта ошибка возникает, потому что существующее значение обновляется сразу после инициализации. Поэтому, если вы обновите новое значение после того, как существующее значение будет отображено в DOM, тогда оно будет работать нормально. Как упоминалось в этой статье, Angular Debugging «Выражение изменилось после того, как оно было проверено»
например, вы можете использовать
}
или
Как видите, я не упомянул время в методе setTimeout. Так как это API, предоставляемый браузером, а не API JavaScript, поэтому он будет работать отдельно в стеке браузера и будет ждать завершения элементов стека вызовов.
Как объясняет концепцию API браузера Филип Робертс в одном из видеороликов Youtube («Что такое взлом цикла обработки событий?»).
источник
Вы также можете поместить свой вызов updateMessage () в метод ngOnInt (), по крайней мере, он работает для меня
В RC1 это не вызывает исключения
источник
Вы также можете создать таймер с помощью
Observable.timer
функции rxjs , а затем обновить сообщение в вашей подписке:источник
Это выдает ошибку, потому что ваш код обновляется при вызове ngAfterViewInit () . Означает, что ваше начальное значение изменилось, когда ngAfterViewInit имеет место. Если вы вызываете это в ngAfterContentInit (), то это не выдаст ошибку.
источник
Я не могу комментировать пост @Biranchi, так как у меня недостаточно репутации, но это исправило проблему для меня.
Стоит отметить одну вещь! При добавлении changeDetection: ChangeDetectionStrategy.OnPush для компонента не работает, и его дочерний компонент (немой компонент), попробуйте добавить его и к родительскому компоненту.
Это исправило ошибку, но мне интересно, каковы побочные эффекты этого.
источник
Я получил аналогичную ошибку при работе с данными. То, что происходит, когда вы используете * ngFor внутри другого * ngFor datatable, генерирует эту ошибку, поскольку она перехватывает цикл угловых изменений. Поэтому вместо использования данных внутри данных используйте одну обычную таблицу или замените mf.data именем массива. Это отлично работает.
источник
Я думаю, что самое простое решение будет следующим:
(static working: boolean)
в классе, где эта функция существует, и каждый раз, когда вы вызываете функцию, просто делайте ее истинной, в зависимости от того, что вам нравится. Внутри функции, если значение работает верно, тогда просто вернитесь сразу, ничего не делая. иначе, выполняйте задание, которое вы хотите. Обязательно измените эту переменную на false, как только задача будет завершена, т.е. в конце строки кодов или в методе подписки, когда вы закончите присваивать значения!источник