我正在阅读有关*ngTemplateOutlet
指令的信息。此指令的使用是通过模板引用和上下文对象作为参数来动态实例化模板。
我想知道的是,我们在Angular中有很多东西可以实现与* ngTemplateOutlet相同的结果,例如:
我们可以有多个*ngIf
,它们可以基于同一组件中的组件变量值来呈现不同的模板。以类似的方式,我们有[ngSwitch]
,它将根据不同的值为我们呈现不同的模板。
我们可以通过引用相应变量的模板引用变量来对*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
指令,还是从中选择一种以获得相同结果的方法?
答案 0 :(得分:3)
您有一个非常有效的问题。如果通过简单的if
或switch
情况可以实现某些目标,为什么我们要使用*ngTemplateOutlet
?
您之所以会想到这些,是因为您正在考虑一个独立的组件级别。换句话说,所有条件,模板都在同一组件中。我们可以根据特定条件轻松选择模板。
动态模板
当我说“库组件”时,它表示通用的可重用组件,例如Autocompleter
或Typeahead
等。这些组件提供了功能部分,但是它们允许开发人员根据需要选择自己的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 article的Angular 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 post的alligator.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