ExpressionChangedAfterItHasBeenCheckedError: Вираз змінився після перевірки. Попереднє значення: 'undefined'


80

Я знаю, що є багато таких самих питань, які вже розміщені в переповненні стека, і я спробував різні рішення, щоб уникнути помилки під час виконання, але жодне з них не працює для мене.

Помилка

Код компонента та HTML

export class TestComponent implements OnInit, AfterContentChecked {
    @Input() DataContext: any;
    @Input() Position: any;
    sampleViewModel: ISampleViewModel = { DataContext: : null, Position: null };
    constructor(private validationService: IValidationService, private modalService: NgbModal, private cdRef: ChangeDetectorRef) {
    }

    ngOnInit() {

    }
    ngAfterContentChecked() {

            debugger;
            this.sampleViewModel.DataContext = this.DataContext;
            this.sampleViewModel.Position = this.Position;

    }


<div class="container-fluid sample-wrapper text-center" [ngClass]="sampleViewModel.DataContext?.Style?.CustomCssClass +' samplewidget-'+ sampleViewModel.Position?.Columns + 'x' + sampleViewModel.Position?.Rows">
     //some other html here
</div>

Зверніть увагу: Цей компонент завантажується динамічно за допомогою DynamicComponentLoader

Після усунення несправностей я виявив кілька проблем

Перш за все цей дочірній компонент завантажується динамічно за допомогою DynamicComponentResolver і передаючи вхідні значення, як показано нижче

 ngAfterViewInit() {
    this.renderWidgetInsideWidgetContainer();

  }


  renderWidgetInsideWidgetContainer() {
    let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
    let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this.widgetHost.viewContainerRef;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
    debugger;
    (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext;
    (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position;

  }

Навіть якщо я змінив свій дочірній компонент html, як показано нижче, я отримую цю саму помилку. Просто додайте кутовий атрибут ngclass

<div class="container-fluid ds-iconwidget-wrapper text-center" [ngClass]="Sample">

</div>

Моя прив'язка даних і все працює нормально. Чи потрібно щось робити на батьківському компоненті? Я вже пробував усі життєві події в дочірньому компоненті


як ви додаєте TestComponent?
Макс Корецький

1
Перш за все переїзд this.renderWidgetInsideWidgetContainer();з ngAfterViewInitдоngOnInit
yurzui

1
@Maximus оцінив ваш час .. Ваша стаття є чудовою
Code-EZ

1
@JEMI, ласкаво просимо) У мене багато цікавих статей, перевірте
Макс Корецький

1
@ Maximus Звичайно. Я вже ваш послідовник. Кожна ваша стаття дуже глибока.
Code-EZ

Відповіді:


48

ngAfterContentCheckedЖиттєвий цикл гак спрацьовує , коли палітурки оновлення для дочірніх компонентів / директив були вже завершено. Але ви оновлюєте властивість, яка використовується як вхідний сигнал для ngClassдирективи. У цьому проблема. Коли Angular запускає етап перевірки, він виявляє, що очікується оновлення властивостей, і видає помилку.

Щоб краще зрозуміти помилку, прочитайте ці дві статті:

Подумайте, чому вам потрібно змінити властивість на ngAfterViewInitгачку життєвого циклу. Будь-який інший життєвий цикл, який запускався раніше ngAfterViewInit/Checked, спрацює, наприклад ngOnInitабо ngDoCheckабо ngAfterContentChecked.

Отже, щоб виправити це, перейдіть renderWidgetInsideWidgetContainerна ngOnInit()гачок життєвого циклу.


чому ви оновлюєте майно в ngAfterViewInit/Checked?
Макс Корецький

Насправді це не оновлення, я встановлюю свої вхідні дані для viewmodel і, нарешті, всі властивості пов'язані через цю модель, як клас обгортки. Коли я безпосередньо прив'язую @Input datacontext, я отримую ту саму помилку. чи немає жодного обхідного шляху, щоб позбутися цієї помилки через будь-яку з подій життєвого циклу. Дозвольте мені спробувати ngAfterViewInit / Checked, згаданий у вашій відповіді
Code-EZ

2
спробувати ngOnInitабо ngDoCheckабо ngAfterContentChecked.якщо він по - , як і раніше не працює, створіть plunker
Макс Корецького

хм ... Це дивно для мене, наскільки я розумію, це досить прямий сценарій. Я підозрюю, що це через динамічний завантажувач компонентів. Дозвольте мені спробувати створити плюнкер.
Code-EZ

Я пробував усі події життєвого циклу, але, на жаль, не зміг вирішити проблему. але моє запитання оновлено, додавши більше балів після мого вирішення проблеми. сподіваюся, ця воля допоможе вам отримати якусь ідею.
Code-EZ

100

Ви повинні повідомити, angularщо оновили вміст після того, ngAfterContentChecked як зможете імпортувати ChangeDetectorRefз нього @angular/coreта зателефонуватиdetectChanges

import {ChangeDetectorRef } from '@angular/core';

constructor( private cdref: ChangeDetectorRef ) {}


ngAfterContentChecked() {

this.sampleViewModel.DataContext = this.DataContext;
this.sampleViewModel.Position = this.Position;
this.cdref.detectChanges();

 }

Перегляньте мої коментарі, про які йдеться, я вже пробував це рішення, але все одно отримую ту ж помилку
Code-EZ

7
Я імпортував AfterContentChecked, який має лише this.cdref.detectChanges (); це працює для мене, дякую
Мохамед Або Ельмагд

@MohamedAboElmagd Чи можете ви написати, як ви змусили це працювати?
Haseeb

1
використовуючи this.cdref.detectChanges (); після будь-якої модифікації
Мохамед Або Ельмагд

його називаються в рази, коли я відкриваю випадаючі елементи та збиваю їх.
Asif vora

11

Якщо ви використовуєте <ng-content>з *ngIfвами, ви обов'язково потрапите в цей цикл.

Єдиний вихід, який я знайшов, - це перехід *ngIfна display:noneфункціональність


або можна використовувати [прихований] = "! умова"
Праґаті Дугар


4

Два рішення:

  1. Переконайтеся, що у вас є деякі змінні прив'язки, перемістіть цей код до settimeout ({}, 0);
  2. Перемістіть пов’язаний код у метод ngAfterViewInit

Це залежить від того, як він переміщує ваш код до черги подій, і він буде виконаний, коли закінчиться поточне виконання. JavaScript є однопотоковою мовою сценаріїв, тому він може виконувати по одній частині коду за раз (завдяки своїй однопотоковій природі) кожен із цих блоків коду "блокує" прогрес інших асинхронних подій. Код встановленого часу буде виконано лише тоді, коли ваш поточний код завершить ваше виконання.
Четан Ладдха,

3

* NgIf може створити тут проблему, тому або використовуйте display none css, або простіший спосіб - використовувати [прихований] = "! Умова"


Чудово! Найкращий і найпростіший спосіб!
Boshra N

2
setTimeout(() => { // your code here }, 0);

Я загорнув свій код у setTimeout, і це спрацювало


2

У мене були проблеми з.

ПОМИЛКА: ExpressionChangedAfterItHasBeenCheckedError: Вираз змінився після перевірки. Попереднє значення для 'mat-checkbox-checked': 'true'. Поточне значення: 'false'.

Проблема тут полягає в тому, що оновлене значення не виявляється до запуску наступного циклу виявлення змін.

Найпростішим рішенням є додавання стратегії виявлення змін. Додайте ці рядки до свого коду:

import { ChangeDetectionStrategy } from "@angular/core";  // import

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "abc",
  templateUrl: "./abc.html",
  styleUrls: ["./abc.css"],
})

1

У мене виникла та сама проблема, коли я намагався зробити щось подібне до вас, і я виправив це чимось подібним до відповіді Річі Фредсіксона.

Коли ви запускаєте createComponent (), він створюється з невизначеними вхідними змінними. Потім після цього, коли ви призначаєте дані цим вхідним змінним, це змінює ситуацію та спричиняє цю помилку у вашому дочірньому шаблоні (у моєму випадку це було тому, що я використовував вхід у ngIf, який змінився після того, як я призначив вхідні дані).

Єдиний спосіб, який я міг би уникнути цього в цьому конкретному випадку, - це примусове виявлення змін після того, як ви призначите дані, проте я не зробив цього в ngAfterContentChecked ().

За вашим прикладом коду трохи важко слідувати, але якщо моє рішення працює для вас, це буде приблизно так (у батьківському компоненті):

export class ParentComponent implements AfterViewInit {
  // I'm assuming you have a WidgetDirective.
  @ViewChild(WidgetDirective) widgetHost: WidgetDirective;

  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private changeDetector: ChangeDetectorRef
  ) {}

  ngAfterViewInit() {
    renderWidgetInsideWidgetContainer();
  }

  renderWidgetInsideWidgetContainer() {
    let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this.widgetHost.viewContainerRef;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
    debugger;
    // This <IDataBind> type you are using here needs to be changed to be the component
    // type you used for the call to resolveComponentFactory() above (if it isn't already).
    // It tells it that this component instance if of that type and then it knows
    // that WidgetDataContext and WidgetPosition are @Inputs for it.
    (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext;
    (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position;
    this.changeDetector.detectChanges();
  }
}

Мій майже такий самий, як той, за винятком того, що я використовую @ViewChildren замість @ViewChild, оскільки у мене є кілька елементів хоста.


0

Спробуйте це, щоб викликати свій код у ngOnInit ()

someMethod() // emitted method call from output
{
    // Your code 
}

ngOnInit(){
  someMethod(); // call here your error will be gone
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.