成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[譯] 探索 Angular 使用 ViewContainerRef 操作 DOM

wind5o / 450人閱讀

摘要:在探索抽象類前,先了解下如何在組件指令中獲取這些抽象類。下面示例描述在組建模板中如何創(chuàng)建如同其他抽象類一樣,通過屬性綁定元素,比如上例中,綁定的是會被渲染為注釋的元素,所以輸出也將是。你可以使用查詢模板引用變量來獲得抽象類。

原文鏈接:Exploring Angular DOM manipulation techniques using ViewContainerRef

如果想深入學(xué)習(xí) Angular 如何使用 Renderer 和 View Containers 技術(shù)操作 DOM,可以查閱 YouTube 視頻 my talk at NgVikings

每次我讀到 Angular 如何操作 DOM 相關(guān)文章時,總會發(fā)現(xiàn)這些文章提到 ElementRefTemplateRefViewContainerRef 和其他的類。盡管這些類在 Angular 官方文檔或相關(guān)文章會有涉及,但是很少會去描述整體思路,這些類如何一起作用的相關(guān)示例也很少,而本文就主要描述這些內(nèi)容。

如果你來自于 angular.js 世界,很容易明白如何使用 angular.js 操作 DOM。angular.js 會在 link 函數(shù)中注入 DOM element,你可以在組件模板里查詢?nèi)魏喂?jié)點(diǎn)(node),添加或刪除節(jié)點(diǎn)(node),修改樣式(styles),等等。然而這種方式有個主要缺陷:與瀏覽器平臺緊耦合。

新版本 Angular 需要在不同平臺上運(yùn)行,如 Browser 平臺,Mobile 平臺或者 Web Worker 平臺,所以,就需要在特定平臺的 API 和框架接口之間進(jìn)行一層抽象(abstraction)。Angular 中的這層抽象就包括這些引用類型:ElementRefTemplateRef、ViewRefComponentRefViewContainerRef。本文將詳細(xì)講解每一個引用類型(reference type)和該引用類型如何操作 DOM。

@ViewChild

在探索 DOM 抽象類前,先了解下如何在組件/指令中獲取這些抽象類。Angular 提供了一種叫做 DOM Query 的技術(shù),主要來源于 @ViewChild@ViewChildren 裝飾器(decorators)。兩者基本功能相同,唯一區(qū)別是 @ViewChild 返回單個引用,@ViewChildren 返回由 QueryList 對象包裝好的多個引用。本文示例中主要以 ViewChild 為例,并且描述時省略 @。

通常這兩個裝飾器與模板引用變量(template reference variable)一起使用,模板引用變量僅僅是對模板(template)內(nèi) DOM 元素命名式引用(a named reference),類似于 html 元素的 id 屬性。你可以使用模板引用(template reference)來標(biāo)記一個 DOM 元素,并在組件/指令類中使用 ViewChild 裝飾器查詢(query)它,比如:

@Component({
    selector: "sample",
    template: `
        I am span
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("tref", {read: ElementRef}) tref: ElementRef;

    ngAfterViewInit(): void {
        // outputs `I am span`
        console.log(this.tref.nativeElement.textContent);
    }
}

ViewChild 裝飾器基本語法是:

@ViewChild([reference from template], {read: [reference type]});

上例中你可以看到,我把 tref 作為模板引用名稱,并將 ElementRef 與該元素聯(lián)系起來。第二個參數(shù) read 是可選的,因?yàn)?Angular 會根據(jù) DOM 元素的類型推斷出該引用類型。例如,如果它(#tref)掛載的是類似 span 的簡單 html 元素,Angular 返回 ElementRef;如果它掛載的是 template 元素,Angular 返回 TemplateRef。一些引用類型如 ViewContainerRef 就不可以被 Angular 推斷出來,所以必須在 read 參數(shù)中顯式申明。其他的如 ViewRef 不可以掛載在 DOM 元素中,所以必須手動在構(gòu)造函數(shù)中編碼構(gòu)造出來。

現(xiàn)在,讓我們看看應(yīng)該如何獲取這些引用,一起去探索吧。

ElementRef

這是最基本的抽象類,如果你查看它的類結(jié)構(gòu),就發(fā)現(xiàn)它只包含所掛載的元素對象,這對訪問原生 DOM 元素很有用,比如:

// outputs `I am span`
console.log(this.tref.nativeElement.textContent);

然而,Angular 團(tuán)隊(duì)不鼓勵這種寫法,不但因?yàn)檫@種方式會暴露安全風(fēng)險(xiǎn),而且還會讓你的程序與渲染層(rendering layers)緊耦合,這樣就很難在多平臺運(yùn)行你的程序。我認(rèn)為這個問題并不是使用 nativeElement 而是使用特定的 DOM API 造成的,如 textContent。但是后文你會看到,Angular 實(shí)現(xiàn)了操作 DOM 的整體思路模型,這樣就不再需要低階 API,如 textContent。

使用 ViewChild裝飾的 DOM 元素會返回 ElementRef,但是由于所有組件掛載于自定義 DOM 元素,所有指令作用于 DOM 元素,所以組件和指令都可以通過 DI(Dependency Injection)獲取宿主元素的ElementRef 對象。比如:

@Component({
    selector: "sample",
    ...
export class SampleComponent{
      constructor(private hostElement: ElementRef) {
          //outputs ...
             console.log(this.hostElement.nativeElement.outerHTML);
      }
    ...

所以組件通過 DI(Dependency Injection)可以訪問到它的宿主元素,但 ViewChild 裝飾器經(jīng)常被用來獲取模板視圖中的 DOM 元素。然而指令卻相反,因?yàn)橹噶顩]有視圖模板,所以主要用來獲取指令掛載的宿主元素。

TemplateRef

對于大部分開發(fā)者來說,模板概念很熟悉,就是跨程序視圖內(nèi)一堆 DOM 元素的組合。在 HTML5 引入 template 標(biāo)簽前,瀏覽器通過在 script 標(biāo)簽內(nèi)設(shè)置 type 屬性來引入模板,比如:

這種方式不僅有語義缺陷,還需要手動創(chuàng)建 DOM 模型,然而通過 template 標(biāo)簽,瀏覽器可以解析 html 并創(chuàng)建 DOM 樹,但不會渲染它,該 DOM 樹可以通過 content 屬性訪問,比如:


I am span in template

Angular 采用 template 標(biāo)簽這種方式,實(shí)現(xiàn)了 TemplateRef 抽象類來和 template 標(biāo)簽一起合作,看看它是如何使用的(譯者注:ng-template 是 Angular 提供的類似于 template 原生 html 標(biāo)簽):

@Component({
    selector: "sample",
    template: `
        
            I am span in template
        
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("tpl") tpl: TemplateRef;

    ngAfterViewInit() {
        let elementRef = this.tpl.elementRef;
        // outputs `template bindings={}`
        console.log(elementRef.nativeElement.textContent);
    }
}

Angular 框架從 DOM 中移除 template 元素,并在其位置插入注釋,這是渲染后的樣子:


    

TemplateRef 是一個結(jié)構(gòu)簡單的抽象類,它的 elementRef 屬性是對其宿主元素的引用,還有一個 createEmbeddedView 方法。然而 createEmbeddedView 方法很有用,因?yàn)樗梢詣?chuàng)建一個視圖(view)并返回該視圖的引用對象 ViewRef。

ViewRef

該抽象表示一個 Angular 視圖(View),在 Angular 世界里,視圖(View)是一堆元素的組合,一起被創(chuàng)建和銷毀,是構(gòu)建程序 UI 的基石。Angular 鼓勵開發(fā)者把 UI 作為一堆視圖(View)的組合,而不僅僅是 html 標(biāo)簽組成的樹。

Angular 支持兩種類型視圖:

嵌入視圖(Embedded View),由 Template 提供

宿主視圖(Host View),由 Component 提供

創(chuàng)建嵌入視圖

模板僅僅是視圖的藍(lán)圖,可以通過之前提到的 createEmbeddedView 方法創(chuàng)建視圖,比如:

ngAfterViewInit() {
    let view = this.tpl.createEmbeddedView(null);
}
創(chuàng)建宿主視圖

宿主視圖是在組件動態(tài)實(shí)例化時創(chuàng)建的,一個動態(tài)組件(dynamic component)可以通過 ComponentFactoryResolver 創(chuàng)建:

constructor(private injector: Injector,
            private r: ComponentFactoryResolver) {
    let factory = this.r.resolveComponentFactory(ColorComponent);
    let componentRef = factory.create(injector);
    let view = componentRef.hostView;
}

在 Angular 中,每一個組件綁定著一個注入器(Injector)實(shí)例,所以創(chuàng)建 ColorComponent 組件時傳入當(dāng)前組件(即 SampleComponent)的注入器。另外,別忘了,動態(tài)創(chuàng)建組件時需要在模塊(module)或宿主組件的 EntryComponents 屬性添加被創(chuàng)建的組件。

現(xiàn)在,我們已經(jīng)看到嵌入視圖和宿主視圖是如何被創(chuàng)建的,一旦視圖被創(chuàng)建,它就可以使用 ViewContainer 插入 DOM 樹中。下文主要探索這個功能。

ViewContainerRef

視圖容器就是掛載一個或多個視圖的容器。

首先需要說的是,任何 DOM 元素都可以作為視圖容器,然而有趣的是,對于綁定 ViewContainer 的 DOM 元素,Angular 不會把視圖插入該元素的內(nèi)部,而是追加到該元素后面,這類似于 router-outlet 插入組件的方式。

通常,比較好的方式是把 ViewContainer 綁定在 ng-container 元素上,因?yàn)?ng-container 元素會被渲染為注釋,從而不會在 DOM 中引入多余的 html 元素。下面示例描述在組建模板中如何創(chuàng)建 ViewContainer

@Component({
    selector: "sample",
    template: `
        I am first span
        
        I am last span
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef;

    ngAfterViewInit(): void {
        // outputs `template bindings={}`
        console.log(this.vc.element.nativeElement.textContent);
    }
}

如同其他抽象類一樣,ViewContainer 通過 element 屬性綁定 DOM 元素,比如上例中,綁定的是 會被渲染為注釋的 ng-container 元素,所以輸出也將是 template bindings={}。

操作視圖

ViewContainer 提供了一些操作視圖 API:

class ViewContainerRef {
    ...
    clear() : void
    insert(viewRef: ViewRef, index?: number) : ViewRef
    get(index: number) : ViewRef
    indexOf(viewRef: ViewRef) : number
    detach(index?: number) : ViewRef
    move(viewRef: ViewRef, currentIndex: number) : ViewRef
}

從上文我們已經(jīng)知道如何通過模板和組件創(chuàng)建兩種類型視圖,即嵌入視圖和組件視圖。一旦有了視圖,就可以通過 insert 方法插入 DOM 中。下面示例描述如何通過模板創(chuàng)建嵌入視圖,并在 ng-container 標(biāo)記的地方插入該視圖(譯者注:從上文中知道是追加到ng-container后面,而不是插入到該 DOM 元素內(nèi)部)。

@Component({
    selector: "sample",
    template: `
        I am first span
        
        I am last span
        
            I am span in template
        
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef;
    @ViewChild("tpl") tpl: TemplateRef;

    ngAfterViewInit() {
        let view = this.tpl.createEmbeddedView(null);
        this.vc.insert(view);
    }
}

通過上面的實(shí)現(xiàn),最后的 html 看起來是:


    I am first span
    
    I am span in template

    I am last span
    

可以通過 detach 方法從視圖中移除 DOM,其他的方法可以通過方法名知道其含義,如通過索引獲取視圖引用對象,移動視圖位置,或者從視圖容器中移除所有視圖。

創(chuàng)建視圖

ViewContainer 也提供了手動創(chuàng)建視圖 API :

class ViewContainerRef {
    element: ElementRef
    length: number

    createComponent(componentFactory...): ComponentRef
    createEmbeddedView(templateRef...): EmbeddedViewRef
    ...
}

上面兩個方法是個很好的封裝,可以傳入模板引用對象或組件工廠對象來創(chuàng)建視圖,并將該視圖插入視圖容器中特定位置。

ngTemplateOutlet 和 ngComponentOutlet

盡管知道 Angular 操作 DOM 的內(nèi)部機(jī)制是好事,但是要是有某種快捷方式就更好了啊。沒錯,Angular 提供了兩種快捷指令:ngTemplateOutletngComponentOutlet。寫作本文時這兩個指令都是實(shí)驗(yàn)性的,ngComponentOutlet 也將在版本 4 中可用(譯者注:現(xiàn)在版本 5.* 也是實(shí)驗(yàn)性的,也都可用)。如果你讀完了上文,就很容易知道這兩個指令是做什么的。

ngTemplateOutlet

該指令會把 DOM 元素標(biāo)記為 ViewContainer,并插入由模板創(chuàng)建的嵌入視圖,從而不需要在組件類中顯式創(chuàng)建該嵌入視圖。這樣,上面實(shí)例中,針對創(chuàng)建嵌入視圖并插入 #vc DOM 元素的代碼就可以重寫:

@Component({
    selector: "sample",
    template: `
        I am first span
        
        I am last span
        
            I am span in template
        
    `
})
export class SampleComponent {}

從上面示例看到我們不需要在組件類中寫任何實(shí)例化視圖的代碼。非常方便,對不對。

ngComponentOutlet

這個指令與 ngTemplateOutlet 很相似,區(qū)別是 ngComponentOutlet 創(chuàng)建的是由組件實(shí)例化生成的宿主視圖,不是嵌入視圖。你可以這么使用:

總結(jié)

看似有很多新知識需要消化啊,但實(shí)際上 Angular 通過視圖操作 DOM 的思路模型是很清晰和連貫的。你可以使用 ViewChild 查詢模板引用變量來獲得 Angular DOM 抽象類。DOM 元素的最簡單封裝是 ElementRef;而對于模板,你可以使用 TemplateRef 來創(chuàng)建嵌入視圖;而對于組件,可以使用 ComponentRef 來創(chuàng)建宿主視圖,同時又可以使用 ComponentFactoryResolver 創(chuàng)建 ComponentRef。這兩個創(chuàng)建的視圖(即嵌入視圖和宿主視圖)又會被 ViewContainerRef 管理。最后,Angular 又提供了兩個快捷指令自動化這個過程:ngTemplateOutlet 指令使用模板創(chuàng)建嵌入視圖;ngComponentOutlet 使用動態(tài)組件創(chuàng)建宿主視圖。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/93544.html

相關(guān)文章

  • [] 關(guān)于 Angular 動態(tài)組件你需要知道的

    摘要:第一種方式是使用模塊加載器,如果你使用加載器的話,路由在加載子路由模塊時也是用的作為模塊加載器。還需注意的是,想要使用還需像這樣去注冊它你當(dāng)然可以在里使用任何標(biāo)識,不過路由模塊使用標(biāo)識,所以最好也使用相同。 原文鏈接:Here is what you need to know about dynamic components in?Angular showImg(https://se...

    lcodecorex 評論0 收藏0
  • 理解Angular2中的ViewContainerRef

    摘要:注意本文不是關(guān)于如何用編程的方式來創(chuàng)建組件的文章。在這個例子中,容器元素就是元素,模版將作為這個元素的兄弟節(jié)點(diǎn)被插入。用來演示以組件自身作為視圖容器,將組件中的模版插入視圖容器的效果。 原文鏈接:https://netbasal.com/angular-...作者:Netanel Basal譯者:而井 showImg(https://segmentfault.com/img/bVbl...

    Codeing_ls 評論0 收藏0
  • 源碼分析 @angular/cdk 之 Portal

    摘要:這些依賴對象也進(jìn)一步暴露了其設(shè)計(jì)思想。關(guān)鍵功能包括在上下文內(nèi)掛載在上下文外掛載在上下文外共享數(shù)據(jù)。在構(gòu)造必須依賴,所以可以直接創(chuàng)建嵌入視圖,然后手動強(qiáng)制執(zhí)行變更檢測。提供了兩個指令和。 @angular/material 是 Angular 官方根據(jù) Material Design 設(shè)計(jì)語言提供的 UI 庫,開發(fā)人員在開發(fā) UI 庫時發(fā)現(xiàn)很多 UI 組件有著共同的邏輯,所以他們把這些共...

    BearyChat 評論0 收藏0
  • Angular操作DOM:意料之外的結(jié)果及優(yōu)化技術(shù)

    摘要:翻譯在中操作意料之外的結(jié)果及優(yōu)化技術(shù)原文鏈接作者譯者而井我最近在的一個研討會上討論了中的高級操作的話題。首先,我會介紹在中操作的工具和方法,然后再介紹一些我在研討會上沒有說過的更高級的優(yōu)化技術(shù)。 【翻譯】在Angular中操作DOM:意料之外的結(jié)果及優(yōu)化技術(shù) 原文鏈接:https://blog.angularindepth.c... 作者:Max Koretskyi 譯者:而井 ...

    未東興 評論0 收藏0
  • 】JavaScript 框架的探索與變遷(下)

    摘要:對此沒有任何限制,它不關(guān)心這個。一種控制變化的辦法是不可改變的,持久化的數(shù)據(jù)結(jié)構(gòu)??偨Y(jié)檢測變化時開發(fā)中的核心問題,而框架們以各種方式解決這個問題。因?yàn)榻M件內(nèi)的變化是不被允許的。 AngularJS:臟檢查 我不知道什么更新了,所以當(dāng)更新的時候,我只能檢查所有的東西。 AngularJS 類似于 Ember,當(dāng)狀態(tài)改變的時候,必須人工去處理。但不同的是,AngularJS 從不同的角度來...

    CollinPeng 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<