如何通过变量调用ng-template?

时间:2019-06-21 02:51:40

标签: angular angular2-template

我检查了: ViewChildren for ng-templateAccess multiple viewchildren using @viewchild

但是我无法通过变量的值来调用我的模板...

所以我的模板是这样的:

<ng-container *ngFor="let feature of Object.values(features)">
  <ng-container *ngTemplateOutlet="templates[feature]"></ng-container>
</ng-container>

<ng-template #myFeature>
  Nothing to see here
</ng-template>

<ng-template #myOtherFeature>
  Nothing to see here
</ng-template>

features是一个枚举,其值与模板名称匹配...然后在我的课堂上,我试图像这样抓住所有ViewChildren

export class SomeClass {
   @ViewChildren(TemplateRef) templates: QueryList<TemplateRef<any>>;
}

因此,我的想法是,我认为我应该能够通过执行templates[feature]来引用正确的模板,该模板应产生类似templates['myFeature']的内容,并为我提供正确的模板...但是不是。

如何存档?

3 个答案:

答案 0 :(得分:0)

由于创建了不同的模板(不同的模板变量),因此需要为每个模板创建不同的视图子级。 ViewChildren仅在它们具有相同的模板引用变量的情况下才能工作。而且您代码中的用法将获取每个模板实例,因为您正在传递TemplateRef,它将获取此类型的每个实例。

我创建了一个堆叠闪电战,展示了this

另外请注意,您的模板实例将仅在ngAfterViewInit()上可用,直到那时它才是未定义的。

答案 1 :(得分:0)

ngAfterViewInit内进行了一些修补之后,我开始按照自己的方式工作。有点丑陋,因为我需要使用setTimeout并且需要使用内部变量(不确定这是否是个好主意)...

这是stackblitz展示柜动态模板的选择和按可变值的呈现。

简而言之,这是我的工作方式,您需要做三件事:

// to grab all the templates
@ViewChildren(TemplateRef) templates: QueryList<TemplateRef<any>>;
// to be use for template selection
templateMap: { [key: string]: TemplateRef<any> } = {};
// to avoid rendering during the first run when templateMap is not yet ready
templateMapReady = false;

然后在ngAfterViewInit中执行以下操作以构建templateMap

ngAfterViewInit(): void {
  // setTimeout to bypass the ExpressionChangedAfterItHasBeenCheckedError
  setTimeout(() => {
    // loop through the fetched template
    this.templates.toArray().forEach(t => {
      // for those ng-template that has a #name, they will have references
      const keys = Object.keys((t as any)._def.references);
      if (keys.length === 1) {
        // so we put these in the templateMap
        this.templateMap[keys[0]] = t;
      }
    });
    // now we change it to ready, so it would render it
    this.templateMapReady = true;
  });
}

答案 2 :(得分:0)

解释

ViewChildren 指令的值将存储在 ngAfterViewInit()see this)之前。

Angular首先检查您的模板,发现templatesundefined

然后开始渲染视图。在此过程中,它将解析ViewChildren()之类的Template指令并调用ngAfterViewInit()

在此过程中,templates被设置,这意味着视图现在处于不一致状态

页面的初始呈现导致页面本身的更改

那是当您收到臭名昭著的“表达式已更改...” 错误。

解决方案

设置为templates时不能更改,这是由Angular精心安排的。
可以进行的操作是,使用另一个变量进行绑定,并在完成初始视图渲染后将其设置为templates

尝试在ngAfterViewInit()中设置新变量将再次触发“表达式已更改”错误,因为此生命周期挂钩本身是初始呈现的一部分。

解决方案是将ngAfterViewInit()中新变量的设置推迟到下一个VM轮换。

为此,我们可以简单地使用setTimeout()而不使用第二个参数:

export class AppComponent implements AfterViewInit {
  @ViewChildren(TemplateRef) templates!: QueryList<TemplateRef<any>>;
  features: TemplateRef<any>[] = [];
  name = "Angular";

  ngAfterViewInit() {
    setTimeout(() => this.features = this.templates.toArray());
  }
}

查看此stackblitz example