Angular中ng-template和ng-container的應用小結
Angular的日常工作中經常會使用到ng-template和ng-container,本文對他們做一個總結。
ng-template
ng-template是一個Angular元素,它不會直接顯示出來。在渲染視圖之前,Angular會把它的內容替換為一個注釋。ng-template是用來定義模板的,當定義好一個模板之后,可以用ng-container和ngTemplateOutlet指令來進行使用。
簡單點來說,就是定義了一個模板片段,并且起了一個名字,在需要的時候可以通過名字來使用這個片段。
<!--定義模板,并不會在視圖中顯示-->
<ng-template #loading>
<div class="waiting-wrap">
<div class="spinner">loading...</div>
</div>
</ng-template>
<!--使用模板,在視圖中顯示-->
<ng-container *ngTemplateOutlet="loading"></ng-container><ng-template #myTemp>
<div>Hello, Tom?</div>
</ng-template>
<div class="container">
<ng-container *ngTemplateOutlet="myTemp"></ng-container>
<div [ngTemplateOutlet]="myTemp">
Jerry
</div>
</div>
作用域的Template
帶作用域的Template,可以從當前作用域獲得數(shù)據。
<ng-template #userTemp let-account="name" let-age="age">
<p>{{account}} - {{age}}</p>
</ng-template>
<ng-container *ngTemplateOutlet="userTemp; context: userInfo">
</ng-container>上面的let-account="name",相當于定義了一個account變量,變量的值為 context.name。也就是下面的代碼:
let account = context.name; let age = context.age;
import { AfterViewInit, Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
import { MsgComponent } from '../msg/msg.component';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss']
})
export class UserComponent implements OnInit, OnChanges, AfterViewInit {
userInfo: any;
constructor() {}
ngOnInit(): void {
this.userInfo = {
name: 'Tom',
age: 21
};
// this.createComponent();
}
}
TemplateRef
TemplateRef實例用于表示模板對象。如果我在模板里定義了一個ng-template,想在組件中訪問它,這時候就用到了TemplateRef。
<!--定義模板,并不會在視圖中顯示--> <ng-template #myTemp> <div>Hello, Tom?</div> </ng-template>
export class UserComponent implements OnInit, OnChanges, AfterViewInit {
@ViewChild('myTemp', { read: TemplateRef}) temp1!: TemplateRef<any>;
......
}TemplateRef:
Represents an embedded template that can be used to instantiate embedded views. To instantiate embedded views based on a template, use the ViewContainerRef method createEmbeddedView().
TemplateRef就是ng-template的實例。后面會說怎么和ng-container的實例搭配使用。
ng-container
ng-container是Angular的一個特殊標簽,它和ng-template完全不同。
- ng-template只是定義了一個視圖片段的模板,并不會直接在視圖中展示出來(會顯示為注釋);
- ng-container可以在視圖中展示出來,其本身并沒有創(chuàng)建任何節(jié)點,只是作為一個邏輯Tag來使用;
<ng-template #myTemp>
<div>Hello, Tom?</div>
</ng-template>
<div class="container">
<ng-container #container1>
Jerry
</ng-container>
</div>
在Vue2中,我們定義一個組件,但是這個組件的視圖中必須有一個根元素,如果有多個根元素,就會報錯。是因為,任何Vue組件的實例需要綁定到一個單一DOM元素上。
<template>
<!--報錯-->
<div>
Tom
</div>
<div>
{{age}}
</div>
</template>
<template>
<!--正確-->
<div>
<div>
Tom
</div>
<div>
{{age}}
</div>
</div>
</template>但是有些情況下,我們就是不想有外層元素包裹,那怎么辦呢?Vue3中改善了這一點,直接可以寫上面的模板,并不會報錯,原因是在模板編譯的時候自動添加了Fragment虛擬元素。
在React中也會有同樣的問題,解決方案就是一個名為Fragment的虛擬元素。
class Columns extends React.Component {
render() {
return (<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}盡管Fragment看起來像一個普通的DOM元素,但它是虛擬的,根本不會在DOM樹中呈現(xiàn)。這樣我們可以將組件實例綁定到一個單一的元素中,而不需要創(chuàng)建一個多余的DOM節(jié)點。
Angular中的ng-container可以看做是Vue,React中的Fragment。
<ul>
<ng-container *ngFor="let item of listdata">
<li>
{{item.id}}---{{item.name}}
</li>
</ng-container>
</ul>那如果我想在組件代碼中操作ng-container的實例呢?
場景:比如在一些復雜場景中,根據不同情況將不同的ng-template插入到ng-container中。
<ng-template #myTemp>
<div>Hello, Tom?</div>
</ng-template>
<div class="container">
<ng-container #container1>
Jerry
</ng-container>
</div>export class UserComponent implements OnInit, OnChanges, AfterViewInit {
// 需要加上{ read: TemplateRef}, { read: ViewContainerRef}
// 否則會當成普通element
@ViewChild('myTemp', { read: TemplateRef}) temp1!: TemplateRef<any>;
@ViewChild('container1', { read: ViewContainerRef}) container1!: ViewContainerRef;
ngAfterViewInit(): void {
console.log(this.temp1);
this.container1?.createEmbeddedView(this.temp1);
}
}
動態(tài)創(chuàng)建Component
說到ng-container,就不得不提下動態(tài)創(chuàng)建Component。
現(xiàn)在有一個組件MsgComponent,我們想在UserComponent中動態(tài)創(chuàng)建出這個Component。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-msg',
template: '<h2>消息:{{msg}}</h2>'
})
export class MsgComponent implements OnInit {
msg?: string = 'Hello';
constructor() { }
ngOnInit() { }
}還要把這個MsgComponent在NgModule的entryComponents里注冊一下。當然也可以在Component的entryComponents里注冊一下也行。
@NgModule({
declarations: [
AppComponent,
UserComponent
],
imports: [
BrowserModule
],
entryComponents: [
MsgComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }在User組件的template中,需要指定一個“放”MsgComponent的地方,也就是ng-container。
<div class="container">
<ng-container #container1>
Jerry
<p>Tom</p>
</ng-container>
</div>import { AfterViewInit, Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
import { MsgComponent } from '../msg/msg.component';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss']
})
export class UserComponent implements OnInit, OnChanges, AfterViewInit {
@ViewChild('container1', { read: ViewContainerRef, static: true}) container1!: ViewContainerRef;
constructor(private _resolveSvc: ComponentFactoryResolver) {
}
ngOnInit(): void {
// console.log('user: ngOnInit');
// this.userID = this.options.id;
this.createComponent();
}
createComponent(): void {
let fac: ComponentFactory<MsgComponent> = this._resolveSvc.resolveComponentFactory(MsgComponent);
// this.container1.clear();
let msgComp: ComponentRef<MsgComponent> = this.container1.createComponent(fac);
msgComp.instance.msg = '動態(tài)創(chuàng)建Component了!';
}
ngAfterViewInit(): void {
// this.createComponent(); // { static: false}
}
}ComponentFactoryResolver是Angular里組件工廠解析器,把這個Service注入進來就可以使用了。resolveComponentFactory接收一個Component類型,生成一個ComponentFactory對象,為什么需要這個ComponentFactory,直接用Component類型不行嗎?因為一個類型其實就是一個Class類,不足以描述一個Component,Component還有selector,inputs,output等,而這個ComponentFactory就包含了這些信息。這里用到了設計模式中的“工廠模式”,輸入一個Component Class,輸出一個對應于這個ComponentFactory具體信息。
createComponent比較好理解了,基于ComponentFactory具體信息,創(chuàng)建一個Component,并把這個Component添加到指定的容器里,還可以指定添加的位置。返回值是組件對象,注意這里是組件對象,并不是組件Class的實例,Class的實例在組件對象的instance屬性上。

如果有需要,可以清空容器。
this.container1.clear();
這里的{ static: true}并不是必須的,也可以不寫static,那就用默認值false。這個static之前說過,如果是false的話,由于在ngOnInit生命周期鉤子的時刻比較早,所以這時候無法訪問到this.container1,就會報錯。那static是false的情況下,應該做實現(xiàn)呢?可以在ngAfterViewInit這個鉤子中實現(xiàn)。
ngAfterViewInit(): void {
this.createComponent();
}但是不建議上面的寫法,因為在ngAfterViewInit里說明View已經完成,這里不建議再次操作View,會影響性能,盡管這樣并不會報錯。那我就是想在這里再次操作View呢?可以把更新推遲到下一個更新周期。
ngAfterViewInit(): void {
setTimeout(() => {
this.createComponent();
});
asyncScheduler.schedule(() => {
this.createComponent();
});
}
有沒有感覺上面動態(tài)創(chuàng)建Component的過程有點復雜,開發(fā)者其實并不關心ComponentFactoryResolver,ComponentFactory這些細節(jié),給你一個Component類型,給我創(chuàng)建出來Component實例就行了。在Angular13中,就對這個地方做了簡化,不在需要ComponentFactoryResolver,ComponentFactory了。
import { AfterViewInit, Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
import { MsgComponent } from '../msg/msg.component';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss']
})
export class UserComponent implements OnInit, OnChanges, AfterViewInit {
@ViewChild('container1', { read: ViewContainerRef, static: true}) container1!: ViewContainerRef;
// 無需注入ComponentFactoryResolver
constructor() {
}
ngAfterViewInit(): void {
// this.createComponent();
}
ngOnInit(): void {
this.createComponent();
}
createComponent(): void {
// V13簡化
let msgComp: ComponentRef<MsgComponent> = this.container1.createComponent(MsgComponent);
msgComp.instance.msg = 'V13動態(tài)創(chuàng)建Component了!';
}
}大功告成!

到此這篇關于Angular中ng-template和ng-container的應用小結的文章就介紹到這了,更多相關Angular中ng-template和ng-container內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
AngularJS使用ng-class動態(tài)增減class樣式的方法示例
這篇文章主要介紹了AngularJS使用ng-class動態(tài)增減class樣式的方法,結合具體實例形式分析了ng-class操作頁面class樣式的相關技巧,需要的朋友可以參考下2017-05-05
Angular-UI Bootstrap組件實現(xiàn)警報功能
這篇文章主要介紹了Angular-UI Bootstrap組件實現(xiàn)警報功能,對Angular.js services的學習有所幫助,需要的朋友可以參考下2018-07-07
使用 Angular RouteReuseStrategy 緩存(路由)組件的實例代碼
這篇文章主要介紹了使用 Angular RouteReuseStrategy 緩存(路由)組件的實例代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-11-11
Angular outlet實現(xiàn)頁面布局示例詳解
這篇文章主要為大家介紹了Angular outlet實現(xiàn)頁面布局示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10

