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
Два решения:
- Убедитесь, что у вас есть некоторые переменные привязки, затем переместите этот код в settimeout ({}, 0);
- Переместите связанный код в метод ngAfterViewInit
Ответ 6
setTimeout(() => { // your code here }, 0);
Я завернул свой код в setTimeout, и это сработало
Ответ 7
Я не понимаю, почему это произошло
Ответ 8
ngAfterViewInit() {
setTimeout(() => {
this.renderWidgetInsideWidgetContainer();
}, 0);
}
Это хорошее решение для решения этой проблемы.