ngIf Expression изменилось после проверки

Я объясню свою проблему на упрощенном примере, так как мой код довольно длинный.

Я хочу отобразить определенный div, когда одно из isFetching или isDownloading истинно.

<div *ngIf="(isFetching || isDownloading)">
  My div element is here
</div>

В начале моей программы оба они верны, поэтому должен отображаться div. Ниже div сверху у меня есть другие элементы, которые отображаются в представлении после изменения isFetching на false. (isFetching заменяется на false после завершения выборки из базы данных.) После того, как isFetching заменяется на false и элементы визуализируются, после загрузки последнего элемента из них (это изображение), я затем меняю isDownloading на false.

Когда оба isFetching и isDownloading ложны, это когда я хочу скрыть элемент div.

Однако код, который я только что объяснил, дает мне эту ошибку:

Expression has changed after it was checked. Previous value: 'ngIf: true'. Current value: 'ngIf: false'.

Кажется, я не понимаю, почему у меня возникает эта проблема? Что не так с моей логикой? Как я могу добиться желаемого результата, не получая эту ошибку?

ИЗМЕНИТЬ:

Этот код находится в конструкторе моего класса:

firebase.database().ref('/users/' + this.userId).once('value').then(function(snapshot) {
  this.username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  this.isFetching = false;
});

После того, как isFetching изменится на false. В моем .html есть и другие элементы, которые отображаются. И затем этот код вызывается после загрузки последнего элемента (который является изображением) этих элементов (load)="latestElementLoaded()".

latestElementLoaded(){
   this.isDownloading = false;
}

person EDJ    schedule 29.07.2018    source источник
comment
Не могли бы вы добавить код, отвечающий за установку как isFetching, так и isDownloading?   -  person user184994    schedule 29.07.2018
comment
Возможный дубликат ngIf — выражение изменилось после проверки   -  person Rodrigo Ferreira    schedule 29.07.2018
comment
@ user184994, я добавил запрошенный вами код.   -  person EDJ    schedule 29.07.2018
comment
Этот комментарий в официальном репозитории может оказаться полезным   -  person Rodrigo Ferreira    schedule 29.07.2018
comment
Вы можете переместить этот код из конструктора в ngOnInit   -  person user184994    schedule 29.07.2018
comment
@RodrigoFerreira, это действительно было очень полезно. Поскольку проблема связана со структурой моего .html, не могли бы вы дать мне совет о том, как реструктурировать мой код, чтобы он выполнял «один проход обнаружения изменений сверху вниз». Я хочу отображать свой div, когда любой из этих isFetchingили isDownloading это true. Поскольку я не могу сделать это с оператором ||, что мне делать дальше? Должен ли я создать еще один элемент, который является дубликатом этого div и к нему прикреплен только *ngIf="isDownloading"? Однако я не думаю, что это лучший обходной путь.   -  person EDJ    schedule 29.07.2018
comment
вы пробовали предложение @user184994? переместить его из конструктора в ngOnInit?   -  person dAxx_    schedule 29.07.2018
comment
@ user184994, я только что попробовал, да. Однако это не исправило ошибку.   -  person EDJ    schedule 29.07.2018


Ответы (1)


Возможно, вы захотите рассмотреть альтернативный шаблон с использованием наблюдаемых, который может упростить ваш код (для этого стоит изучить AngularFire):

this.data$ = this.angularFirestore.doc('some/path').valueChanges();

Это получает ваши данные как наблюдаемые (это просто демонстрационный код - проверьте фактический синтаксис). Поместите это в ngOnInit, а не в конструктор. Не нужно возиться с .once, снимками или другим шаблонным кодом.

Теперь в своем шаблоне вы можете написать

<div *ngIf="data$ | async as data; else notFetched">
  Data is {{data | json}}
  <ng-template #notFetched>Data is not fetched yet..wait...</ng-template>
</div>

Это полностью устраняет необходимость в isFetching-подобных флагах.

Что касается вашего кода:

firebase.database().ref('/users/' + this.userId).once('value').then(function(snapshot) {
  this.username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  this.isFetching = false;
});

Я понятия не имею, как это вообще будет работать, поскольку this внутри функции обратного вызова (function(snapshot)), вероятно, будет неопределенным, что приведет к ошибке во время выполнения.

Я не эксперт по внутренностям Angular, но я подозреваю, что причиной ошибки, о которой вы сообщаете, является следующая последовательность событий:

  1. Создается экземпляр компонента и запускается конструктор.
  2. Запускается запрос Firebase.
  3. Вызывается ngOnInit и проверяются обнаруженные изменения переменных. isFetching по-прежнему верно на данный момент.
  4. Angular делает больше работы по созданию представления и того, что у вас есть.
  5. Тем временем запрос Firebase завершается, а для isFetching устанавливается значение false.
  6. Прежде чем завершить этот раунд рендеринга, Angular проверяет еще раз (он делает это в тестовом режиме), просто чтобы убедиться, что ничего неожиданно не изменилось. Но это так!

Вся эта проблема вызвана размещением логики запроса firebase в конструкторе, как указано в комментарии. Конструкторы должны быть строго ограничены базовой инициализацией. Они не должны содержать бизнес-логику и определенно не должны содержать асинхронных вещей.

person Community    schedule 29.07.2018