簡體   English   中英

如何通過變量調用ng-template?

[英]How to call ng-template by variable?

我檢查了: ViewChildren for ng-template and Access 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']並給我正確的模板......但不是。

我該如何存檔?

由於您創建了不同的模板(不同的模板變量),您需要為每個模板創建不同的子視圖。 ViewChildren 僅在它們具有相同的模板引用變量時才起作用。 並且在您的代碼中的用法它將獲取每個模板實例,因為您正在傳遞TemplateRef ,它將獲取此類型的每個實例。

我創建了一個 stackblitz,它演示了這一點

另請注意,您的模板實例僅在ngAfterViewInit()上可用,在ngAfterViewInit()之前它將是未定義的。

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;
  });
}

解釋

ViewChildren指令將在ngAfterViewInit()之前存儲其值(請參閱this )。

Angular 首先檢查你的模板並發現templatesundefined

然后它開始渲染視圖。 在這個過程中,它解析模板指令,如ViewChildren()並調用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());
  }
}

請參閱此堆棧閃電戰示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM