ExpressionChangedAfterItHasBeenCheckedError: выражение было изменено после его проверки. Предыдущее значение: 'undefined'

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

Error

Компонент и 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 своего дочернего компонента, как показано ниже, я получаю ту же ошибку. Просто добавьте angular атрибут ngclass

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

</div>

Моя база данных и все работают нормально. Нужно ли что-то делать с родительским компонентом? Я уже перепробовал все события жизненного цикла в дочернем компоненте

Ответ 1

ngAfterContentChecked жизненного цикла ngAfterContentChecked запускается, когда обновления привязок для дочерних компонентов/директив уже завершены. Но вы обновляете свойство, которое используется в качестве входного связывания для директивы ngClass. Это проблема. Когда Angular запускает этап проверки, он обнаруживает ожидающее обновление свойств и выдает ошибку.

Чтобы лучше понять ошибку, прочитайте эти две статьи:

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

Таким образом, чтобы исправить это, переместите renderWidgetInsideWidgetContainer в ngOnInit() жизненного цикла ngOnInit().

Ответ 2

вы должны сообщить 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();

 }

Ответ 3

У меня возникла та же проблема, когда я пытался сделать что-то такое же, как и ты, и я исправил это с помощью чего-то похожего на ответ Ричи Фредиксона.

Когда вы запускаете 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, так как у меня есть несколько элементов хоста.

Ответ 4

Если вы используете <ng-content> с *ngIf вы обязательно *ngIf в этот цикл.

Единственный выход, который я нашел, это изменить *ngIf для display:none функциональности

Ответ 5

Два решения:

  1. Убедитесь, что у вас есть некоторые переменные привязки, затем переместите этот код в settimeout ({}, 0);
  2. Переместите связанный код в метод ngAfterViewInit

Ответ 6

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

Я завернул свой код в setTimeout, и это сработало

Ответ 7

Я не понимаю, почему это произошло

Ответ 8

 ngAfterViewInit() {
   setTimeout(() => {
     this.renderWidgetInsideWidgetContainer();
   }, 0);
  }

Это хорошее решение для решения этой проблемы.