在检查之后,表达式已更改。先前值:'ngTemplateOutlet: undefined'。当前值:'ngTemplateOutlet:[object Object]'。

4

出现了这样的错误:

错误 Error: ExpressionChangedAfterItHasBeenCheckedError: 表达式在变更检查完成后发生了变化。原始值:'ngTemplateOutlet: undefined'。当前值:'ngTemplateOutlet: [object Object]'。

位于 viewDebugError (core.js:9775)

expressionChangedAfterItHasBeenCheckedError (core.js:9753)

checkBindingNoChanges (core.js:9920)

checkNoChangesNodeInline (core.js:13970)

checkNoChangesNode (core.js:13942)

debugCheckNoChangesNode (core.js:14771)

debugCheckDirectivesFn (core.js:14673)

Object.eval [as updateDirectives] (ShowEventComponent.html:73)

Object.debugUpdateDirectives [as updateDirectives] (core.js:14655)

checkNoChangesView (core.js:13780)

她的出现是由此引起的:

<tr *ngFor="let user of users">
    <ng-template [ngTemplateOutlet]="loadTemplate(user)" 
                [ngTemplateOutletContext]="{ $implicit: user}">
    </ng-template>
</tr>

我该如何解决这个问题?

我使用的是Angular 5.2.0和rxjs 5.5.6。


1
关于这个问题的好文章:https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4 - Envil
另一个类似的SO问题:https://dev59.com/JlkS5IYBdhLWcg3we22Z - Envil
这些都没有帮助到。 - user10031243
这种类型的问题不容易解决,你必须了解Angular变更检测机制的工作原理,然后从那里应用你的知识到你的项目中,并找出关键问题。 - Envil
5个回答

8

这与变化检测有关。您可以在此处阅读更多信息。我通过使用来自ChangeDetectorRef的detectChanges()方法解决了此问题。

constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit() {}

  ngAfterViewInit(): void {
    this.cdr.detectChanges();
  }

您需要实现AfterViewInit接口。


这个方法可行,但不是立即生效的,Angular有点奇怪,可能是某种内部缓存系统,等一会儿就好了。 - Edgar Quintero

5

我最近解决了一个类似的问题,由于找不到太多帮助,所以我想提供一些帮助。

首先,这只会在开发模式下出现,但这是一个严重的问题。当Angular运行变更检测时,它会跟踪所有绑定的值(例如ngTemplateOutlet),然后将该值与变更检测循环结束后进行比较。

您之所以会收到此错误是因为您在html中设置了[ngTemplateOutlet]的方法。这会导致该方法(loadTemplate(user))每次发生变更检测时运行。这很可能是该方法运行的5到10倍,这并不理想。

第一次通过时,该值设置为未定义,并且在随后的周期中,该值得到更新。我猜测loadTemplate返回TemplateRef,但当方法第一次运行时,TemplateRef未定义,这就是原因。

有很多方法可以解决这个问题,我需要看看您的component.ts代码。但是,必须进行2个更改。首先,您需要使用不使用方法来设置[ngTemplateOutlet]。您可以使用组件Observable或组件字段。但是,您还需要在正确的时间设置该字段或observable。您要延迟直到模板定义。您可以在ngOnInit中通过setTimeout()包装设置字段的代码,或者如果使用Observable,则可以使用可让操作rxjs的delay(0)。

同样,我需要查看loadTemplate(user)和component.ts中的逻辑,以确切地说明如何安排它。

您还可以从父组件向此组件输入所需的数据,然后在[ngTemplate]之后使用该数据进行逻辑处理


2

就像Envil发布的那样,您可以将loadTemplate()函数的内容包装在setTimeout中,例如:

    setTimeout(() => {
        yourTemplateLoadingMechanism();
    });

一般来说,我建议避免在模板中调用函数,因为它们会被反复调用,这使得调试困难并且会影响性能。相反,你可以预取所有的模板,并像通常一样提供双向绑定,甚至使用带有 async 关键字的 rxjs Observables ,这样可以更有效地解决你的问题。


1

当在一个名称为loadTemplate(user)的函数中,你要返回TemplateRef时,应该这样获取它:

@ViewChild('readOnlyTemplate', { static: true }) readOnlyTemplate!: TemplateRef<any>

在编程中,指定{ static: true }非常重要。


1

您应该在 AfterContentInit 函数中设置 TemplateRef


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接