ViewChild 共有 3 種使用方式

@ViewChild() decorator 一開始是在存取 child component 時學到的,事實上 @ViewChild() 還有其他使用方法。

Version


Node.js 8.9.3
Angular CLI 1.6.2
Angular 5.2

Component


使用 @ViewChild() 存取 child component。

viewchild000

  • counter 初始值為 2
  • +counter + 1,按 -counter -1

Architecture

viewchild001

  • AppComponent 負責處理 <button/>CounterComponent 負責顯示 counter
  • AppComponent 相當於 parent component,CounterComponent 相當於 child component
  • AppComponent 直接呼叫 CounterComponent 的 method 改變 counterAppComponent 也可直接讀取 CounterComponent 的 property

Implementation

AppComponent

viewchild002

app.component.html

1
2
3
4
<button (click)="onAddOneClick()">+</button>
<button (click)="onMinusOneClick()">-</button>
<p></p>
<app-counter></app-counter>

將 2 個 <button/> 留在 AppComponent 內。

counter 的顯示由 <app-counter/> 負責。

app.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { CounterComponent } from './counter/counter.component';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
@ViewChild(CounterComponent)
private counterComponent: CounterComponent;

ngAfterViewInit(): void {
console.log(this.counterComponent.counter);
}

onAddOneClick() {
this.counterComponent.addOne();
}

onMinusOneClick() {
this.counterComponent.minusOne();
}
}

10 行

1
2
@ViewChild(CounterComponent)
private counterComponent: CounterComponent;

Angular 允許我們在 parent component 透過 @ViewChild() decorator 宣告 child component,藉此存取 child component 的 public field 與 method。

@ViewChild() 第一個參數傳入 child component 的型別。

第 9 行

1
2
3
4
5
export class AppComponent implements AfterViewInit {
ngAfterViewInit(): void {
console.log(this.counterComponent.counter);
}
}

若要透過 @ViewChild() 存取 child component 的 public field,則必須在 ngAfterViewInit() lifecycle hook 才可抓的到,不可以在 ngOnInit()

17 行

1
2
3
onAddOneClick() {
this.counterComponent.addOne();
}

若要透過 @ViewChild() 存取 child component 的 public method,則無此限制。

CounterComponent

viewchild003

counter.component.html

1
{{ counter }}

負責顯示 counter

counter.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Component } from '@angular/core';

@Component({
selector: 'app-counter',
templateUrl: './counter.component.html',
styleUrls: ['./counter.component.css']
})
export class CounterComponent {
counter = 2;

addOne() {
this.counter++;
}

minusOne() {
this.counter--;
}
}

沒有特別的部分需要講解。

viewchild004

當 parent component 要存取 child component 時,使用 @ViewChild() 宣告 child decorator,public field 可在 ngAfterViewInit() 存取,public method 則無此限制

Directive

當使用 directive 套用在 HTML 上時,可以藉由 @ViewChild() 取得 directive 的物件實體,藉此控制 directive。

Implementation

ChangeColorDirective

change-color.directive.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { AfterViewInit, Directive, ElementRef } from '@angular/core';

@Directive({
selector: '[appChangeColor]'
})
export class ChangeColorDirective implements AfterViewInit {

constructor(private elementRef: ElementRef) {
}

ngAfterViewInit(): void {
this.elementRef.nativeElement.style.color = 'red';
}

change(changedColor: string) {
this.elementRef.nativeElement.style.color = changedColor;
}
}

使用 Angular CLI 的 ng g d ChangeColor 建立 ChangeColor directive。

15 行

1
2
3
change(changedColor: string) {
this.elementRef.nativeElement.style.color = changedColor;
}

ChangeColor directive 提供了 change() 修改 color

AppComponent

app.component.html

1
2
3
<p appChangeColor>{{ title }}</p>
<p></p>
<button (click)="onChangeColorClick()">Change Color</button>

<p> 套用了 ChangeColor directive。

app.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component, ViewChild } from '@angular/core';
import { ChangeColorDirective } from './change-color.directive';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
@ViewChild(ChangeColorDirective)
private changeColorDirective: ChangeColorDirective;
title = 'app';

onChangeColorClick() {
this.changeColorDirective.change('green');
}
}

10 行

1
2
@ViewChild(ChangeColorDirective)
private changeColorDirective: ChangeColorDirective;

為了使用 ChangeColor directive 的 change(),特別使用 @ViewChild() 取得 ChangeColor directive 的物件實體,如此就能使用 change()

DOM Element


最後一個常用的 @ViewChild() 方式,是藉由 template reference varibale 存取 DOM element。

Implementation

AppComponent

app.component.html

1
2
3
4
5
Name: <input type="text" #name>
<p></p>
<button (click)="onSubmitClick()">Submit</button>
<p></p>
{{ yourName }}

若要在 JavaScript 存取 HTML,傳統會使用 id 或 CSS selector,在 Angular 提出新的方法,我們可以為 HTML 加上 # 開頭的 template reference variable。

app.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component, ElementRef, ViewChild } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
yourName: string;

@ViewChild('name')
private name: ElementRef;

onSubmitClick() {
this.yourName = this.name.nativeElement.value;
}
}

11 行

1
2
@ViewChild('name')
private name: ElementRef;

使用 @ViewChild() 取得 DOM element 的物件實體,參數以字串傳入 template reference variable 的字串名稱。

注意其型別為 ElementRef

14 行

1
2
3
onSubmitClick() {
this.yourName = this.name.nativeElement.value;
}

如此就能在 TypeScript 藉由 @ViewChild() 所宣告的變數,存取 DOM element 物件。

Conclusion


  • 傳統都只將 @ViewChild() 用在存取 child component,事實上 @ViewChild() 還可以用在存取 directive 與 DOM element。

Sample Code


完整的範例可以在我的 GitHub 上找到

Reference


Arvind Rai, Angular 2 @ViewChild() Example

2018-01-18