1 背景
使用公司前端組件庫時,用到了@viewchild,一臉懵逼,場景就是通過模板引用變量獲取了對應的模板實例及其對應的ts實例。經過學習,發現,@ViewChild和@ViewChildren是Angular提供給我們的裝飾器,用於從模板視圖中獲取匹配的元素。獲取模板元素的操作是在在父組件鉤子方法ngAfterViewInit調用之前進行的。
2 基本用法
@ViewChild(入參) 變量名:類名;
這個裝飾器的直接目的,就是通過入參來獲得模板背後的某個類實例,通過對獲得類實例進行操作從而實現DOM操作或者其他邏輯。
入參的類型的有:1、模板引用變量名,2、public權限的類名,比如component、directive還有一些組件主動provider出來的類,3、TemplateRef,本質是還是一個類,只是第二種類型的特殊情況,組件是angular自定義的而已。根據兩種情況分別舉兩個例子:
入參是模板引用變量
<!--兩個-->
<my-component #cmp1="myComponent"></my-component>
<my-component #cmp2模板引用變量></my-component>
@ViewChild('cmp1') var1:myComponent;
@ViewChild('cmp2') var2:myComponent;
//一些var1和var2的操作……
入參是類名
//一個模板和組件類定義在一起的一個組件
import {Component, OnInit} from '@angular/core';
import {ChildService} from './child.service';
@Component({
selector: 'app-child',
template: `
<h1>自定義的一個子組件</h1>
`,
providers: [
ChildService
]
})
export class ChildComponent implements OnInit {
constructor(public childService: ChildService) {
}
ngOnInit() {
}
}
<app-child></app-child>
//對html中的app-child對應的類實例和暴露出來的服務類進行使用
@ViewChild(ChildService) service:ChildService;
@ViewChild(ChildComponent ) component:ChildComponent ;
//一些對service和component的操作
入參是TemplateRef
當選擇器是TemplateRef(模板引用)的時候,則會獲取到html裏面所有的ng-template類型的節點。實際例子如下:
@ViewChild(TemplateRef) template: TemplateRef<any>;
@ViewChildren(TemplateRef) templateList: QueryList<TemplateRef<any>>;
但是!!!!!TemplateRef也只不過是一類特殊的組件/類而已。
2.1 小結
最本質的!入參直接是一個的模板引用變量名的情況下,能取到的對應的組件實例肯定只有一個,這樣使用即可:
@ViewChild('cmp1') var1:myComponent;var1:myComponent;
入參是是一個class名,比如自己定義的組件class、 組件provider出來的class、TemplateRef等等,這樣使用:
@ViewChild(ChildService) service:ChildService;
@ViewChildren(TemplateRef) templateList: QueryList<TemplateRef<any>>;
使用@ViewChild,按照類型取DOM時,如果存在多個,只取出現的第一個;想全部取到的話,就用@ViewChildren;
所以,本質上,用模板引用變量還是根據類型取DOM,根據適合的場景進行選擇。
3 代碼實踐
先寫了一個自定義組件,MyInputComponent,選擇器是 app-my-input
<p>
my-input works!
手動實現一個的輸入框
</p>
<hr>
<mat-form-field>
<input matInput [placeholder]="placeHolder" [(ngModel)]="inputValue">
</mat-form-field>
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-my-input',
templateUrl: './my-input.component.html',
styleUrls: ['./my-input.component.css']
})
export class MyInputComponent implements OnInit {
public placeHolder: string;
public inputValue: string;
constructor() {
}
ngOnInit() {
this.placeHolder = 'Default Holder';
}
}
暴露出兩個的屬性,讓父組件進行讀取、使用。
在父組件中對這個自定義組件進行調用:
<hr>
<span>模板引用變量 input1</span>
<app-my-input #input1></app-my-input><br>
<span>從本組件的屬性中讀取的值爲:{{input1_in_main_component.inputValue}}</span>
<hr>
<span>模板引用變量 input2</span><br>
<app-my-input #input2></app-my-input>
<span>直接從模板引用變量中讀取的值爲:{{input2.inputValue}}</span>
<hr>
<span>模板引用變量 input3</span>
<app-my-input #input3></app-my-input>
<ol>
<li *ngFor="let eachInput of inputList;">
{{eachInput.placeHolder}}<br>{{eachInput.inputValue}}
</li>
</ol>
@ViewChild('input1') input1_in_main_component: MyInputComponent;
@ViewChildren(MyInputComponent) inputList: MyInputComponent[];
想通過viewchild的方式訪問模板引用變量input1的屬性值,並且對得到的實例進行調用,在頁面上進行顯示。
對於模板引用變量input2,可以直接在頁面上訪問、使用其屬性值。
同時,通過ViewChildren實現了對模板頁面上MyInputComponent組件的DOM選取,成功讀取了自定義組件中的屬性placeholder和inputValue的值。
最終,實現效果如下: