閱讀 Angular 6/RxJS 最新教程,請訪問 前端修仙之路
ViewChild 是屬性裝飾器,用來從模板視圖中獲取匹配的元素。視圖查詢在 ngAfterViewInit 鉤子函數調用前完成,所以在 ngAfterViewInit 鉤子函數中,就能正確獲取查詢的元素。前端
@ViewChild 使用模板變量名數組
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; @Component({ selector: 'my-app', template: ` <h1>Welcome to Angular World</h1> <p #greet>Hello {{ name }}</p> `, }) export class AppComponent { name: string = 'Semlinker'; @ViewChild('greet') greetDiv: ElementRef; ngAfterViewInit() { console.dir(this.greetDiv); } }
@ViewChild 使用模板變量名及設置查詢條件app
import { Component, TemplateRef, ViewChild, ViewContainerRef, AfterViewInit } from '@angular/core'; @Component({ selector: 'my-app', template: ` <h1>Welcome to Angular World</h1> <template #tpl> <span>I am span in template</span> </template> `, }) export class AppComponent { @ViewChild('tpl') tplRef: TemplateRef<any>; @ViewChild('tpl', { read: ViewContainerRef }) tplVcRef: ViewContainerRef; ngAfterViewInit() { console.dir(this.tplVcRef); this.tplVcRef.createEmbeddedView(this.tplRef); } }
@ViewChild 使用類型查詢函數
child.component.tsui
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'exe-child', template: ` <p>Child Component</p> ` }) export class ChildComponent { name: string = 'child-component'; }
app.component.tsthis
import { Component, ViewChild, AfterViewInit } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ selector: 'my-app', template: ` <h4>Welcome to Angular World</h4> <exe-child></exe-child> `, }) export class AppComponent { @ViewChild(ChildComponent) childCmp: ChildComponent; ngAfterViewInit() { console.dir(this.childCmp); } }
以上代碼運行後,控制檯的輸出結果:spa
ViewChildren 用來從模板視圖中獲取匹配的多個元素,返回的結果是一個 QueryList 集合。prototype
@ViewChildren 使用類型查詢code
import { Component, ViewChildren, QueryList, AfterViewInit } from '@angular/core'; import { ChildComponent } from './child.component'; @Component({ selector: 'my-app', template: ` <h4>Welcome to Angular World</h4> <exe-child></exe-child> <exe-child></exe-child> `, }) export class AppComponent { @ViewChildren(ChildComponent) childCmps: QueryList<ChildComponent>; ngAfterViewInit() { console.dir(this.childCmps); } }
以上代碼運行後,控制檯的輸出結果:component
@ViewChild 示例
import { Component, ElementRef, ViewChild } from '@angular/core'; @Component({ selector: 'my-app', template: ` <h1>Welcome to Angular World</h1> <p #greet>Hello {{ name }}</p> `, }) export class AppComponent { name: string = 'Semlinker'; @ViewChild('greet') greetDiv: ElementRef; }
編譯後的 ES5 代碼片斷
var core_1 = require('@angular/core'); var AppComponent = (function () { function AppComponent() { this.name = 'Semlinker'; } __decorate([ core_1.ViewChild('greet'), // 設定selector爲模板變量名 __metadata('design:type', core_1.ElementRef) ], AppComponent.prototype, "greetDiv", void 0);
ViewChildDecorator 接口
export interface ViewChildDecorator { // Type類型:@ViewChild(ChildComponent) // string類型:@ViewChild('tpl', { read: ViewContainerRef }) (selector: Type<any>|Function|string, {read}?: {read?: any}): any; new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChild; }
ViewChildDecorator
export const ViewChild: ViewChildDecorator = makePropDecorator( 'ViewChild', [ ['selector', undefined], { first: true, isViewQuery: true, descendants: true, read: undefined, } ], Query);
makePropDecorator函數片斷
/* * 建立PropDecorator工廠 * * 調用 makePropDecorator('ViewChild', [...]) 後返回ParamDecoratorFactory */ function makePropDecorator(name, props, parentClass) { // name: 'ViewChild' // props: [['selector', undefined], // { first: true, isViewQuery: true, descendants: true, read: undefined}] // 建立Metadata構造函數 var metaCtor = makeMetadataCtor(props); function PropDecoratorFactory() { var args = []; ... // 轉換arguments對象成args數組 if (this instanceof PropDecoratorFactory) { metaCtor.apply(this, args); return this; } ... return function PropDecorator(target, name) { var meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {}; meta[name] = meta.hasOwnProperty(name) && meta[name] || []; meta[name].unshift(decoratorInstance); Reflect.defineMetadata('propMetadata', meta, target.constructor); }; var _a; } if (parentClass) { // parentClass: Query PropDecoratorFactory.prototype = Object.create(parentClass.prototype); } ... return PropDecoratorFactory; }
makeMetadataCtor 函數:
// 生成Metadata構造函數: var metaCtor = makeMetadataCtor(props); // props: [['selector', undefined], // { first: true, isViewQuery: true, descendants: true, read: undefined }] function makeMetadataCtor(props) { // metaCtor.apply(this, args); return function ctor() { var _this = this; var args = []; ... // 轉換arguments對象成args數組 props.forEach(function (prop, i) { // prop: ['selector', undefined] var argVal = args[i]; if (Array.isArray(prop)) { // argVal: 'greet' _this[prop[0]] = argVal === undefined ? prop[1] : argVal; } else { // { first: true, isViewQuery: true, descendants: true, read: undefined } // 合併用戶參數與默認參數,設置read屬性值 for (var propName in prop) { _this[propName] = argVal && argVal.hasOwnProperty(propName) ? argVal[propName] : prop[propName]; } } }); }; }
咱們能夠在控制檯輸入 window['__core-js_shared__'] ,查看經過 Reflect API 保存後的metadata信息
接下來咱們看一下編譯後的 component.ngfactory.js 代碼片斷,查詢條件 @ViewChild('greet')
咱們再來看一下前面示例中,編譯後 component.ngfactory.js 代碼片斷,查詢條件分別爲:
1.@ViewChild('tpl', { read: ViewContainerRef })
2.@ViewChild(ChildComponent)
經過觀察不一樣查詢條件下,編譯生成的 component.ngfactory.js 代碼片斷,咱們發現 Angular 在建立 AppComponent 實例後,會自動調用 AppComponent 原型上的 createInternal 方法,纔開始建立組件中元素,因此以前咱們在構造函數中是獲取不到經過 ViewChild 裝飾器查詢的視圖元素。另外,配置的視圖查詢條件,默認都會建立一個 jit_QueryList 對象,而後根據 read 查詢條件,建立對應的實例對象,而後添加至 QueryList 對象中,而後在導出對應的查詢元素到組件對應的屬性中。
ViewChild 裝飾器用於獲取模板視圖中的元素,它支持 Type 類型或 string 類型的選擇器,同時支持設置 read 查詢條件,以獲取不一樣類型的實例。而 ViewChildren 裝飾器是用來從模板視圖中獲取匹配的多個元素,返回的結果是一個 QueryList 集合。