Angular - @ViewChild && template refs && ElementRef"

Posted by Tim Lin on 2019-01-25

ElementRef

  • A wrapper around a native element inside of a View.
  • ElementRef 是 view 裡面的 native element(dom element) 的一個包裝

這裡在 input 後面加上了 template #refs: #email

1
2
3
4
5
6
7
8
9
10
11
12
template: `
<div>
<form (ngSubmit)="onSubmit(form.value)" #form="ngForm">
...
<label>
Email address
<input type="email" name="email" ngModel #email>
</label>
...
</form>
</div>
`

並且使用 @ViewChild 來存取這個 input

1
@ViewChild('email') email: ElementRef;

ElementRef 裡面有一個 nativeElement

所以如果把 this.email.nativeElement 印出來看看

1
2
3
ngAfterViewInit() {
console.log(this.email.nativeElement);
}

會看到 nativeElement 就是代表這個 dom element

我們就可以直接對這個 dom element 直接操作

第一個設定了 placeholder
第二個設定了一個 css class
第三個設定了 focus 在這個欄位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ngAfterViewInit() {
console.log(this.email.nativeElement);

this.email.nativeElement.setAttribute('placeholder', 'Enter your email address');
this.email.nativeElement.classList.add('email');
this.email.nativeElement.focus();

if (this.message) {
this.message.forEach((message) => {
message.days = 30;
});
this.cd.detectChanges();
}
}
1
2
3
styles: [`
.email { border-color: #9f72e6; }
`],

Result

所以最後的結果就會是這樣

auth.form.compoent.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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import { Component, ChangeDetectorRef, ElementRef, Output, ViewChild, ViewChildren, AfterViewInit, EventEmitter, ContentChildren, QueryList, AfterContentInit } from '@angular/core';

import { AuthRememberComponent } from './auth-remember.component';
import { AuthMessageComponent } from './auth-message.component';

import { User } from './auth-form.interface';

@Component({
selector: 'auth-form',
styles: [`
.email { border-color: #9f72e6; }
`],
template: `
<div>
<form (ngSubmit)="onSubmit(form.value)" #form="ngForm">
<ng-content select="h3"></ng-content>
<label>
Email address
<input type="email" name="email" ngModel #email>
</label>
<label>
Password
<input type="password" name="password" ngModel>
</label>
<ng-content select="auth-remember"></ng-content>
<auth-message
[style.display]="(showMessage ? 'inherit' : 'none')">
</auth-message>
<ng-content select="button"></ng-content>
</form>
</div>
`
})
export class AuthFormComponent implements AfterContentInit, AfterViewInit {

showMessage: boolean;

@ViewChild('email') email: ElementRef;

@ViewChildren(AuthMessageComponent) message: QueryList<AuthMessageComponent>;

@ContentChildren(AuthRememberComponent) remember: QueryList<AuthRememberComponent>;

@Output() submitted: EventEmitter<User> = new EventEmitter<User>();

constructor(private cd: ChangeDetectorRef) {}

ngAfterViewInit() {
console.log(this.email.nativeElement);

this.email.nativeElement.setAttribute('placeholder', 'Enter your email address');
this.email.nativeElement.classList.add('email');
this.email.nativeElement.focus();

if (this.message) {
this.message.forEach((message) => {
message.days = 30;
});
this.cd.detectChanges();
}
}

ngAfterContentInit() {
if (this.remember) {
this.remember.forEach((item) => {
item.checked.subscribe((checked: boolean) => this.showMessage = checked);
});
}
}

onSubmit(value: User) {
this.submitted.emit(value);
}

}

app.compoent.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
25
26
27
28
29
30
31
32
33
34
35
import { Component } from '@angular/core';

import { User } from './auth-form/auth-form.interface';

@Component({
selector: 'app-root',
template: `
<div>
<auth-form (submitted)="loginUser($event)">
<h3>Login</h3>
<auth-remember (checked)="rememberUser($event)"></auth-remember>
<button type="submit">
Login
</button>
</auth-form>
</div>
`
})
export class AppComponent {

rememberMe: boolean = false;

rememberUser(remember: boolean) {
this.rememberMe = remember;
}

createUser(user: User) {
console.log('Create account', user);
}

loginUser(user: User) {
console.log('Login', user, this.rememberMe);
}

}

Reference