从查询列表中识别特定的Angular TemplateRef

时间:2018-10-20 08:35:06

标签: angular angular-template angular-template-variable

在Angular 6/7中,我有一个向其中投射内容的组件( ParentComponent 模板):

<my-component [templateNames]="['t1', 't2']">
  <ng-template name="t1">...</ng-template>
  <ng-template name="t2">...</ng-template>
  <ng-template>...</ng-template> <!-- not included in [templateNames] -->
</my-component>

MyComponent类中,我可以使用 ContentChildren 装饰器获得所有模板的 QueryList

@ContentChildren(TemplateRef) templates: QueryList<TemplateRef<any>>;

挑战在于,我想在通过@Input() templateNames设置的 ParentComponent 所标识的特定模板上执行代码。

processTemplates() {
  for (const name of this.templateNames) {
    const templateRef = this.getTemplateByName(name);
    this.doWork(templateRef);
  }
}

getTemplateByName(name) {
  const templates = this.templates.toArray();

  return templates.find(t => ?); // what can I query to distinguish templates?
}

问题在于我不知道如何读取name属性或在 ParentComponent 中的 ng-template 标记上设置的其他内容。我不知道如何区分一个TemplateRef与另一个;

请记住, MyComponent 无法对将使用什么名称或是否应处理所有 ng-templates 做出任何假设,这是我示例中的最后一个上面的内容不会在 @Input()templateNames 中列出,因此不会被处理。我可以在 ParentComponent 中进行设置,以帮助我区分两个 TemplateRef 的区别吗?

2 个答案:

答案 0 :(得分:2)

您可以使用名称输入参数创建指令:

@Directive({
  selector: '[template-name]'
})
export class TableColumnDirective {

  constructor(public readonly template: TemplateRef<any>) { }

  @Input('template-name') columnName: string;
}

这样使用:

  <my-component>
      <ng-template template-name="t1">...</ng-template>
      <ng-template template-name="t2">...</ng-template>
      ...

然后在 my-component 中以这种方式注入:

@ContentChildren(TableColumnDirective) templates: QueryList<TableColumnDirective>;

有关更详细的解释/示例,请查看已接受的答案 from this question

答案 1 :(得分:1)

您可以选择以下其中一种方法:

如果仅用于2个组件,则可以使用QueryList getter(第一个和最后一个)访问它们

@ContentChildren(TemplateRef) templates: QueryList<TemplateRef<any>>;

ngAfterContentInit() {
    console.log(this.templates.first);    // Gives you the 1st template child
    console.log(this.templates.last);     // Last template child (2nd child)     
}

按索引查找

this.templates.find((template, index) => index == 1); // 2nd template child

其他替代方法

使用组件的扩展名创建了Stackblitz Demo

1。)创建 TemplateContentComponent ,它将用作您的 ChildComponent 并添加@Input()

    @Component({
      selector: 'template-content',
      template: `
          // If no ng-template reference available, show its ng-content
          <ng-content *ngIf="!template"></ng-content>

         // Else, show the ng-template through ng-container
         <ng-container *ngIf="template"
                       [ngTemplateOutlet]="template"></ng-container>
      ` 
    })
    export class TemplateContentComponent {
        @Input() name: string;    // Serves as your component id
    }

2。)创建 TemplateContainerComponent -这将用作您的 ParentComponent

 @Component({
  selector: 'template-container',
  template: `<ng-content></ng-content>`
})
export class TemplateContainerComponent implements AfterContentInit  {

    @ContentChildren(TemplateContentComponent) templates: QueryList<TemplateRef<any>>;

      ngAfterContentInit() {
        // You can now check whether you'll be fetching a template
        // based on the names you want provided from parent template.

        const t1 = this.templates.find((template: any) => template.name === 't1');

        console.log(t1);   // This will show the t1 component
                           // which t1 and t2 are the same component
                           // but had set a name @Input() as their ID
      }

    }

Result

3。)在您的 AppComponent 模板

<template-container>
  // Can be a raw template, good for ng-content
  <template-content [name]="'t1'">t1 template</template-content>

  // Or a template from ng-template, good for ng-container
  <template-content [name]="'t2'"
                    [template]="userList"></template-content>
</template-container>


// User List Template
<ng-template #userList>
  <h1>User List</h1>
</ng-template>

Template