Angular6 學習筆記——內容投影, ViewChild和ContentChild


angular6.x系列的學習筆記記錄,仍在不斷完善中,學習地址:

https://www.angular.cn/guide/template-syntax

http://www.ngfans.net/topic/12/post/2

 

系列目錄

(1)組件詳解之模板語法

(2)組件詳解之組件通訊

(3)內容投影, ViewChild和ContentChild

(4)指令

(5)路由

 

內容投影

1為什么需要內容投影?

一個事物的出現,必然存在它所能解決的問題,讓我們先從問題出發吧:

大家應該都知道,在html規范里面,它定義了非常多的標簽,在這些標簽里面,相同標簽之間的嵌套,不同標簽之間的嵌套,是十分常見,並且可行

同時,在Angular里面,我們可以通過自定義標簽的方式引用組件,那么這里的標簽能否像原生的html標簽一樣,來嵌入html標簽,或者嵌套其他組件標簽呢?

於是就引入我們今天的主要問題,用一個詳細的例子來描述吧:

假設存在父組件Content,和它下面2個子組件PartA和PartB,自定義標簽分別為:<app-content>,<app-content-part-a>,<app-content-part-b>,目錄結構如下

如果想在父組件的視圖里面,完成下面的內容,是否可行呢?

 content.component.html 

1
<div> 2 <div>Content</div> 3 <div> 4 <app-content-part-a> 5 <h1>PartA--start</h1> 6 <app-content-part-b></app-content-part-b> 7 <span>PartA--end</span> 8 </app-content-part-a> 9 </div> 10 </div>

這樣是不行的,其結果只會顯示自定義的組件<app-content-part-a>自身的內容,因為自定義組件標簽會忽略嵌套其中的html原生標簽或者其他的自定義組件標簽,從而使它們無法產生任何效果

2如何使用內容投影?

上述問題通過內容投影則能夠解決,那么如何使用內容投影呢?

只需要在組件PartA的視圖里面做一些改動,內容如下

 part-a.component.html

1
<div> 2 <div> 3 <ng-content select="h1"></ng-content> 4 </div> 5 <div> 6 <ng-content select="app-content-part-b"></ng-content> 7 </div> 8 <div> 9 <ng-content select="span"></ng-content> 10 </div> 11 </div>

經過這樣的修改,上述想要實現的效果就可以達到

那么內容投影是如何工作的呢?

首先通過angular里面的一個指令ng-content,實現占位,再通過select,達到選擇器的作用,這樣在組件生命周期過程,初始渲染投影內容的時候,就能夠將對應的內容投影到特定的位置,這就是內容投影工作的簡單描述

組件里面嵌套組件,之間的通訊問題可以參考組件間的通訊

 

ContentChild和ViewChild                                                                                 

首先做個簡單的介紹:

ContentChild:與內容子節點有關,操作投影進來的內容;

ViewChild:與視圖子節點有關,操作自身的視圖內容;

在上一部分,我們通過內容投影,讓自定義的組件標簽能夠嵌入html標簽或自定義組件標簽,那么它如何操作投影進來的內容呢?

還是以上述內容為例,從實際的問題出發:假設嵌入的自定義組件標簽<app-content-part-b>里面聲明了一個方法func(),那么如何在<app-content-part-a>里面去操作這個方法呢?
上面說過,ContentChild是操作投影進來的內容,那么在這里我們也可以通過它解決問題,在組件PartA內,通過ContentChild獲取投影進來的組件PartB,並對它進行操作(部分代碼在上一部分已經貼出,這一部分不予重復),代碼如下

 part-b.component.ts

1
import { Component, OnInit,Output} from '@angular/core'; 2 3 @Component({ 4 selector: 'app-content-part-b', 5 templateUrl: './part-b.component.html', 6 styleUrls: ['./part-b.component.scss'] 7 }) 8 export class PartBComponent implements OnInit { 9 constructor() { } 10 11 ngOnInit() { 12 } 13 14 public func():void{ 15 console.log("PartB"); 16 } 17 }
 part-a.component.ts

1
import { Component, OnInit, ContentChild } from '@angular/core'; 2 import { PartBComponent } from '../part-b/part-b.component'; 3 4 @Component({ 5 selector: 'app-content-part-a', 6 templateUrl: './part-a.component.html', 7 styleUrls: ['./part-a.component.scss'] 8 }) 9 export class PartAComponent implements OnInit { 10 11 @ContentChild(PartBComponent) PartB:PartBComponent 12 13 constructor() { } 14 15 ngOnInit() {} 16 17 ngAfterContentInit(): void { 18 this.PartB.func(); 19 } 20 }

這里需要注意一點:在組件的生命周期里面,有一個鈎子ngAfterContentInit()是與投影內容初始化有關,所以我們有關投影的內容操作盡量放在它初始化完成之后進行

 如果理解了ContentChild的用法,那么ViewChild幾乎沒有理解難度,他們的差異不大,所不同的是:

  1ViewChild是操作視圖本身存在的節點,而不是投影進來的內容

  2ngAfterContentInit()對應的是ngAfterViewInit()(視圖節點初始化是在投影內容初始化之后)

其他沒有什么不同,這里我就不再贅述

 ContentChildViewChild還存在復數的形式,即ContentChildrenViewChildren,它們取到的是節點的一個集合,其他的沒有什么區別

寫法如下:

 1 import { Component, OnInit, ContentChild,ContentChildren ,QueryList } from '@angular/core';
 2 import { PartBComponent } from '../part-b/part-b.component';
 3 
 4 @Component({
 5   selector: 'app-content-part-a',
 6   templateUrl: './part-a.component.html',
 7   styleUrls: ['./part-a.component.scss']
 8 })
 9 export class PartAComponent implements OnInit {
10 
11 @ContentChildren(PartBComponent)
12 PartBs: QueryList<PartBComponent>;
13 
14   constructor() { }
15 
16   ngOnInit() {}
17 
18 }

上述代碼中PartBs是組件PartB的一個集合,這就是復數的用法,ViewChildren不再贅述

 

 (終)

 

文檔信息

 


感謝您的閱讀,如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕。本文歡迎各位轉載,但是轉載文章之后必須在文章頁面中給出作者和原文連接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM