* ngTemplateOutlet指令的实际方案是什么?

时间:2018-10-20 18:30:05

标签: angular directive ng-template

我正在阅读有关*ngTemplateOutlet指令的信息。此指令的使用是通过模板引用和上下文对象作为参数来动态实例化模板。

我想知道的是,我们在Angular中有很多东西可以实现与* ngTemplateOutlet相同的结果,例如:

  1. 我们可以有多个*ngIf,它们可以基于同一组件中的组件变量值来呈现不同的模板。以类似的方式,我们有[ngSwitch],它将根据不同的值为我们呈现不同的模板。

  2. 我们可以通过引用相应变量的模板引用变量来对*ngIf使用引用。

对于前一种情况:

<div *ngIf="condition1"> Content 1 </div>
<div *ngIf="condition2"> Content 2 </div>
<div *ngIf="condition3"> Content 3 </div>

对于后者:

<ng-container *ngIf="condition then myTemplate else otherTemplate"></ng-container>
<ng-template #myTemplate> Some content... </ng-template>
<ng-template #otherTemplate> Some content... </ng-template>

如果我们的武器库中有这样的方法,*ngTemplateOutlet还能增加什么价值?

在哪些实际使用案例中(如果有的话)我们不能使用上述方法,而应该使用*ngTemplateOutlet指令,还是从中选择一种以获得相同结果的方法?

4 个答案:

答案 0 :(得分:3)

您有一个非常有效的问题。如果通过简单的ifswitch情况可以实现某些目标,为什么我们要使用*ngTemplateOutlet

独立组件

您之所以会想到这些,是因为您正在考虑一个独立的组件级别。换句话说,所有条件,模板都在同一组件中。我们可以根据特定条件轻松选择模板。

库组件

  

动态模板

当我说“库组件”时,它表示通用的可重用组件,例如AutocompleterTypeahead等。这些组件提供了功能部分,但是它们允许开发人员根据需要选择自己的template他们的需求。

这就是问题所在,这些模板并不位于Autocompleter,而是来自其@ContentChild

例如:

<ng-autocompleter>
   <ng-template #item let-item>{{item.name}}</ng-template>
<ng-autocompleter>

在上面的示例中,<ng-template>被定义为开发人员稍后的时间,而不是<ng-autocompleter>的直接部分。

  

模板上下文

每当开发高度配置的组件时,模板上下文就非常重要。获得动态模板(html)不足以达到目的。我们需要将该值绑定到ng-template。由于ng-template不在ng-autocompleter的一部分中,因此我们需要传递包含所有需要绑定的数据的上下文。

ex:在上述情况下,如果您看到我们通过item声明了let-item变量,但是item的来源。这将取决于赋予*ngTemplateOutlet的上下文。

  

一行结论   如果我们想注入将来会被某人声明的模板,那么我无法通过* ngIf或* ngSwitch处理这种情况。我们需要使用*ngTemplateOutlet

答案 1 :(得分:3)

角度template outlets可用于在视图的各个部分中插入通用模板,这些模板不是由循环生成或受条件约束的。例如,您可以定义公司徽标的模板,然后将其插入页面的多个位置:

<div>
  <ng-container *ngTemplateOutlet="companyLogoTemplate"></ng-container>
  <h1>Company History</h1>
  <div>{{companyHistory}}</div>
</div>
<form (ngSubmit)="onSubmit()">
  <ng-container *ngTemplateOutlet="companyLogoTemplate"></ng-container>
  <h1>User info</h1>
  <label>Name:</label><input type="text" [(ngModel)]="userName" />
  <label>Account ID:</label><input type="text" [(ngModel)]="accountId" />
  <button>Submit</button>
</form>
<div class="footer">
  <ng-container *ngTemplateOutlet="companyLogoTemplate"></ng-container>
</div>

<ng-template #companyLogoTemplate>
  <div class="companyLogo">
    <img [src]="logoSourceUrl">
    <label>The ACME company, {{employeeCount}} people working for you!</label>
  </div>
</ng-template>

模板和模板出口还可以帮助使组件可配置。以下示例摘自this articleAngular University

选项卡容器组件定义了默认的选项卡头模板,但允许使用定义为输入属性的自定义模板覆盖它。然后,将适当的模板(默认模板或自定义模板)插入带有模板出口的视图中:

@Component({
  selector: 'tab-container',
  template: `
    <ng-template #defaultTabButtons>
      <div class="default-tab-buttons">
        ...
      </div>
    </ng-template>
    <ng-container *ngTemplateOutlet="headerTemplate || defaultTabButtons"></ng-container>
    ... rest of tab container component ...
  `
})
export class TabContainerComponent {
    @Input() headerTemplate: TemplateRef<any>; // Custom template provided by parent
}

在父组件中,定义自定义选项卡头模板并将其传递给选项卡容器组件:

@Component({
  selector: 'app-root',
  template: `      
    <ng-template #customTabButtons>
      <div class="custom-class">
        <button class="tab-button" (click)="login()">
          {{loginText}}
        </button>
        <button class="tab-button" (click)="signUp()">
          {{signUpText}}
        </button>
      </div>
    </ng-template>
    <tab-container [headerTemplate]="customTabButtons"></tab-container>      
  `
})
export class AppComponent implements OnInit {
  ...
}

您可以在this blog postalligator.io中看到另一个高级用例。

答案 2 :(得分:1)

使* ngTemplateOutlet比* ng-content更强大的一件事是当您将其与[ngTemplateOutletContext]输入一起使用时。这使您可以创建一个完全唯一的模板,该模板可以使用组件中的状态。

我已使用它创建了一个选择组件,该组件通过不同的模板为每个客户端设置了唯一的样式,但是共享完全相同的代码。这是StackBlitz link

* ngTemplateOutlets与使用* ngIfs相比的一个好处示例是,如果组件仅由单个客户端使用,则您的组件不需要依赖外部包,即Icon lib。

答案 3 :(得分:0)

ngTemplateOutlet 真正的强大之处在于,当您需要处理不知道可以深入多少层的嵌套项时。在处理嵌套数组时,执行 ngFor 或 ngSwitch 很麻烦,而且有限,因为您总是需要事先知道要深入多少层:

例如:

如果这是我的数组:

  todo = [
    'Get to work',
    [
      'Get up',
      'Brush teeth',
      'Take a shower',
      'Check e-mail',
      'Walk dog'
    ],
    [
      'Prepare for work',
      'Drive to office',
      'Park car',
      'Work',
      [
        'See Clients',
        'Fill Out Forms',
        'Take Tea Breaks',
        [
          'Switch on Kettle',
          'Get Clean Cup',
          'Add Tea',
          'Pour Water',
          'Drink'
        ]
      ]
    ],
    'Pick up groceries',
    'Go home',
    'Fall asleep'
  ];

那么您可以使用以下方法:

<div *ngFor="let item of todo" style="margin-left: 25px">

  <div *ngIf="!isArray(item); else arrayView">{{item}}</div>
  <ng-template #arrayView>

    <div *ngFor="let item2 of item" style="margin-left: 25px">

      <p>And So On...</p>

    </div>

  </ng-template>

</div>

但是如果你不知道你的数组有多少层深怎么办?您可以将 ngTemplateOutlet 与 ng-template 结合使用,以解决问题:

<ng-template #tmplItems let-todo="todo">

  <div *ngFor="let item of todo" style="margin-left: 25px">

    <div *ngIf="!isArray(item); else arrayView">{{item}}</div>
    <ng-template #arrayView>

      <ng-container *ngTemplateOutlet="tmplItems,context:{todo:item}">

      </ng-container>

    </ng-template>

  </div>

</ng-template>

<div *ngFor="let item of todo">

  <div *ngIf="!isArray(item); else arrayView">{{item}}</div>
  <ng-template #arrayView>

    <ng-container *ngTemplateOutlet="tmplItems,context:{todo:item}">

    </ng-container>

  </ng-template>

</div>

现在无论您的数组深入多少层都没有关系,递归调用您的 ng-template 可以解决问题(除非存在我尚未意识到的限制)。

输出:

Get to work
    Get up
    Brush teeth
    Take a shower
    Check e-mail
    Walk dog
    Prepare for work
    Drive to office
    Park car
    Work
        See Clients
        Fill Out Forms
        Take Tea Breaks
            Switch on Kettle
            Get Clean Cup
            Add Tea
            Pour Water
            Drink
Pick up groceries
Go home
Fall asleep