Angular5.0 的事件訂閱
Angular 範例程式中,最有名(?)的除了官網的 Hero 之外,還有 TodoMVC ,
進入到 Angular5.0 之後,當然也不能免俗的要再改寫一次。
在 Angular5.0,最重要的改變就是引進了 RxJS 的 Observable 用法;
也就是 Observable(被觀察者) / Observer(觀察者)。
觀察者樣式最常見的實作範例就是 “事件” ,
在程式中可以訂閱按鈕的 click 事件 – Observer(觀察者)
按鈕被按下之後,clickEvent 會被拋出 – 按鈕是 Observable(被觀察者)
在 Angular4.0裡,是使用 Promoise 來實作非同步的資料提供者 - TodoService,
以下是4.0寫法:(範例僅保留關鍵,非完整程式)
src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class TodoService {
private todos: ToDo[] = [];
constructor() { }
get() {
return new Promise(resolve => resolve(todos));
}
}
src/app/todo/todo.component.ts
import { Component, OnInit } from '@angular/core';
import { TodoService } from './todo.service';
export class TodoComponent implements OnInit {
private todos;
constructor(private todoService: TodoService) { }
getTodos(){
return this.todoService.get().then(todos => {
this.todos = todos;
});
}
ngOnInit() {
this.getTodos();
}
}
到了 5.0,改用 Observable 的寫法是:
TodoService src/app/todo/todo.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from "rxjs";
import { Observable } from "rxjs/Observable";
@Injectable()
export class TodoService {
private todos: ToDo[] = [];
private lastId: number = 0;
private _todos: BehaviorSubject<ToDo[]>;
constructor() { }
get(): Observable<ToDo[]> {
return this._todos;
}
add(data: ToDo) {
this.todos.push(data);
this._todos.next(this.todos);
}
}
觀察者訂閱事件方式:
src\app\todo\todo.component.ts
import { TodoService } from ‘./todo.service’;
export class TodoComponent implements OnInit {
todos: ToDo[];
constructor(private toDoService: TodoService) { }
ngOnInit() {
this.getTodos();
}
getTodos() {
this.toDoService.get().subscribe(
{
next: (todos) => {
this.todos = todos;
}
}
);
}
}
RxJS 的被觀察者有 3 種通知狀態:
1. “next”:發送目前數值;內容可為 number / string/ object…;觀察者可以透過此狀態取得新內容
2. “error”:發送錯誤內容
3. “complete”:通知已經執行完成,不會再發送新的數值;如果需要知道執行的工作何時完成,可以訂閱此狀態
觀察者也是透過同名狀態取得資料。
RxJS 裡的 Observable 有 4 種類型:
1. Subject:最基本的 Observable ,可有多個觀察者。
2. BehaviorSubject:Subject 的變形,內部會保留最新狀態,當有新的觀察者訂閱時,會立刻發送目前狀態。若使用 Subject ,在被觀察者送出狀態後才訂閱的觀察者,已經錯過之前狀態的通知,只能等到下次通知時才會獲得被觀察者的狀態。
從範例來看:
import { BehaviorSubject } from 'rxjs';
var subject = new BehaviorSubject(0); // 需要給初始值
subject.subscribe({
next: (v) => console.log('observerA: ' + v)
});
// print: observerA: 0 // 雖然還沒有數值變化,訂閱立刻就會收到當下最新狀態
subject.next(1);
// print: observerA: 1
subject.next(2);
// print: observerA: 2
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
});
subject.next(3);
// print: observerA: 3
// print: observerB: 3 // 訂閱當下 B 就會收到當下最新狀態
也來看看 Subject 的範例:
import { Subject } from ‘rxjs’;
var subject = new Subject();
subject.subscribe({
next: (v) => console.log('observerA: ' + v)
});
subject.next(1);
// print: observerA: 1
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
});
subject.next(2);
// print: observerA: 2
// print: observerB: 2 // 只會收到訂閱後的數值變化
3 . ReplaySubject:BehaviorSubject 的變形,不僅後到的觀察者會取得先前已發送過的狀態,根據 buffer 長度,可以獲得前 N 次的狀態。
ReplaySubject() {
const subject = new ReplaySubject(2); // 宣告儲存舊狀態的 buffer 長度為 2
subject.subscribe({
next: (v) => {
console.log(‘observerA: ’ + v)
}
});
subject.next(1);
// print: observerA: 1
subject.next(2);
// print: observerA: 2
subject.next(3);
// print: observerA: 3
subject.subscribe({
next: (v) => console.log('observerB: ' + v)
});
// print: observerB: 2
// observerB: 3 // 因為 buffer 長度為 2 ,會記錄前兩次的狀態,發送給新訂閱者
subject.next(4);
// print: observerA: 4
// observerB: 4
}
4. AsyncSubject:非同步的通知者;只有當工作完成後,觀察者才會收到通知。var subject = new AsyncSubject();
subject.subscribe({
next: (v) => console.log(‘observerA: ’ + v)
});
subject.next(1);
subject.next(2);
subject.subscribe({
next: (v) => console.log(‘observerB: ’ + v)
});
subject.next(3);
subject.complete();
// print: observerA: 3
// observerB: 3
以上範例來自 RxJS 官網
在 ToDo 範例中,選擇使用 BehaviorSubject。
以上解釋,如有錯誤或不清楚的地方,歡迎指教!
留言