Angular 事件

事件 (EventEmitter)

事件繫結 (Event Binding)

事件(Event) 可以說是一種狀態的描述,它提供一個主動通知的機制,讓我們可以在發生的當下去做一些額外的處理,例如:按下按鈕,這應該是我們最常用了事件,在點擊按鈕後我們可能儲存目前資訊、切換目前頁面、顯示額外訊息…等等。

Angular UI:標題列 我們從官方範例一起複製了一段代碼-(click)="sidenav.open()",看起來就像是點擊事件,但是又跟 DOM 的點擊事件(onclick)比起來少了最前面的 on 多了小括弧。

Angular 內建透過指令(Directive)方式產生了相對應 DOM 事件的 偽事件 (不是正確觀念,但是這樣說好像比較好理解), 它本身會去監控 DOM 的事件,當 DOM 事件觸發時在去觸發對應的 偽事件,初期我們可以直接把它當作 DOM 事件來看待,只是寫法上稍做調整。
小括號 其實是 Angular 單向繫結(事件繫結)的語法,透過繫結方式讓 前端樣板(xxx**.component.html**) 的變化( 偽事件的觸發) 可以反應回 後端元件(xxx**.component.ts**)。

img

建立自訂事件

我們在 src\app\home\header\header.component.ts 內宣告一個事件觸發器(EventEmitter),並透過 @Output() 這個裝飾器將它提供給外部引用,並建立一個 sidenav_click 方法來觸發 EventEmitter

@Output() 可以加入別名參數,外部會引用到此別名,它會直接對應到原本的參數,預設別名與參數相同,一般除非是特別規畫過,否則直接使用預設值在維護上會比較方便。

header.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Component, OnInit, Output, EventEmitter} from '@angular/core';

@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
@Output() sidenavClick = new EventEmitter();

constructor() { }

ngOnInit() {
}

sidenav_click() {
this.sidenavClick.emit(null);
}
}

開啟 src\app\home\header\header.component.html 將 menu 按鈕複製過來,將 click 改連結到 sidenav_click 方法,這樣 HeaderComponent 就多了一個 sidenavClick 的事件。

header.component.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<md-toolbar color="primary">
<button md-icon-button (click)="sidenav_click()" fxHide fxShow.xs>
<md-icon>menu</md-icon>
</button>
<span>Angular demo</span>
<span fxFlex></span>
<button md-icon-button mdTooltip="首頁" routerLink="/home">
<md-icon>home</md-icon>
</button>
<button md-icon-button mdTooltip="設定">
<md-icon>settings_input_component</md-icon>
</button>
<button md-icon-button mdTooltip="關於">
<md-icon>info</md-icon>
</button>
</md-toolbar>

開啟 src\app\home\home.component.html,移除 menu 按鈕,並將 sidenav.open() 繫結至 HeaderComponentsidenavClick

home.component.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div fxFill fxLayout="column" class="layout">
<div class="header" fxLayout="row">
<!-- <button md-icon-button (click)="sidenav.open()" fxHide fxShow.xs>
<md-icon>menu</md-icon>
</button> -->
<app-header fxFlex (sidenavClick)="sidenav.open()"></app-header>
</div>
<div fxFlex fxLayout="row" class="main">
<div fxFlex="200px" class="aside" fxHide.xs>
<app-aside></app-aside>
</div>
<md-sidenav-container fxFlex>
<md-sidenav #sidenav mode="over" class="aside" fxHide fxShow.xs>
<app-aside fxFlex="200px"></app-aside>
</md-sidenav>
<div fxFlex class="content">
<router-outlet></router-outlet>
</div>
</md-sidenav-container>
</div>
</div>

以手機尺寸執行瀏覽器查看結果。
img

同樣方式我們在 AsideComponent 元件內建立一個 menuClick 事件,並讓它在點選功能選單時觸發。

aside.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Component, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
selector: 'app-aside',
templateUrl: './aside.component.html',
styleUrls: ['./aside.component.scss']
})
export class AsideComponent implements OnInit {
@Output() menuClick = new EventEmitter();

constructor() { }

ngOnInit() {
}

menu_click() {
this.menuClick.emit(null);
}
}

aside.component.html
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
<md-list>
<h3 md-subheader>員工專區</h3>
<button md-button routerLink="./calendar" (click)="menu_click()">
<md-list-item>
<md-icon md-list-icon>today</md-icon>
<h4 md-line>行 事 曆</h4>
</md-list-item>
</button>
<button md-button routerLink="./address-book" (click)="menu_click()">
<md-list-item>
<md-icon md-list-icon>contact_phone</md-icon>
<h4 md-line>通 訊 錄</h4>
</md-list-item>
</button>
<button md-button routerLink="./logbook" (click)="menu_click()">
<md-list-item>
<md-icon md-list-icon>border_color</md-icon>
<h4 md-line>工作日誌</h4>
</md-list-item>
</button>
<button md-button routerLink="./to-do-list" (click)="menu_click()">
<md-list-item>
<md-icon md-list-icon>playlist_add_check</md-icon>
<h4 md-line>待辦事項</h4>
</md-list-item>
</button>
<button md-button routerLink="./file" (click)="menu_click()">
<md-list-item>
<md-icon md-list-icon>cloud_download</md-icon>
<h4 md-line>檔案下載</h4>
</md-list-item>
</button>
<button md-button routerLink="./leave" (click)="menu_click()">
<md-list-item>
<md-icon md-list-icon>weekend</md-icon>
<h4 md-line>請 假</h4>
</md-list-item>
</button>
<button md-button routerLink="./reimburse" (click)="menu_click()">
<md-list-item>
<md-icon md-list-icon>attach_money</md-icon>
<h4 md-line>差旅報支</h4>
</md-list-item>
</button>
</md-list>

開啟 src\app\home\home.component.html,將 sidenav.close() 繫結至 AsideComponentmenuClick

home.component.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div fxFill fxLayout="column" class="layout">
<div class="header" fxLayout="row">
<!-- <button md-icon-button (click)="sidenav.open()" fxHide fxShow.xs>
<md-icon>menu</md-icon>
</button> -->
<app-header fxFlex (sidenavClick)="sidenav.open()"></app-header>
</div>
<div fxFlex fxLayout="row" class="main">
<div fxFlex="200px" class="aside" fxHide.xs>
<app-aside (menuClick)="sidenav.close()"></app-aside>
</div>
<md-sidenav-container fxFlex>
<md-sidenav #sidenav mode="over" class="aside" fxHide fxShow.xs>
<app-aside fxFlex="200px" (menuClick)="sidenav.close()"></app-aside>
</md-sidenav>
<div fxFlex class="content">
<router-outlet></router-outlet>
</div>
</md-sidenav-container>
</div>
</div>

重新執行瀏覽器查看結果,現在點選功能選單時 sidenav 就會自己藏起來,整體效果應該跟一般手機APP操作差不多了。
img

[**first-app_2017-09-07.zip**](/uploads/first-app_2017-09-07.zip)