Angular - 響應式程式設計



響應式程式設計是一種處理資料流和變化傳播的程式設計正規化。資料流可以是靜態的或動態的。靜態資料流的一個例子是陣列或資料集合。它將具有初始數量,並且不會改變。動態資料流的一個例子是事件發射器。事件發射器在事件發生時發射資料。最初,可能沒有事件,但隨著時間的推移,事件會發生並被髮射。

響應式程式設計使資料流能夠從一個稱為Observable的源發射,並透過稱為訂閱的過程由其他稱為Observer的源捕獲。這種 Observable/Observer 模式或簡單的Observer模式極大地簡化了程式設計環境中複雜的變化檢測和必要的更新。

JavaScript 本身並不支援響應式程式設計。RxJs是一個 JavaScript 庫,它使 JavaScript 中的響應式程式設計成為可能。Angular 廣泛使用RxJs庫來執行以下高階概念:

  • 元件之間的資料傳輸。
  • HTTP 客戶端。
  • 路由器。
  • 響應式表單。

讓我們在本節中使用RxJs庫學習響應式程式設計。

Observable

如前所述,Observable是資料來源,它們可以是靜態的或動態的。Rxjs提供了許多方法從常見的 JavaScript 物件建立Observable。讓我們看看一些常見的方法。

of - 按順序發射任意數量的值,最後發射完成通知。

const numbers$ = of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

這裡,

  • numbers$是一個Observable物件,訂閱時將按順序發射 1 到 10。

  • 變數末尾的美元符號 ($)用於標識該變數是 Observable。

range: 按順序發射一個範圍內的數字。

const numbers$ = range(1,10)

from: 發射陣列、Promise 或可迭代物件。

const numbers$ = from([1,2,3,4,5,6,7,8,9,10]);

ajax: 透過 AJAX 獲取 URL,然後發射響應。

const api$ = ajax({ url: 'https://httpbin.org/delay/1', method: 'POST', headers: { 'Content-Type': 'application/text' }, body: "Hello" });

這裡,

https://httpbin.org 是一項免費的 REST API 服務,它將以 JSON 格式返回提供的正文內容,如下所示:

{ 
   "args": {}, 
   "data": "Hello", 
   "files": {}, 
   "form": {}, 
   "headers": { 
      "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 
      "Accept-Encoding": "gzip, deflate, br", 
      "Accept-Language": "en-US,en;q=0.9", 
      "Host": "httpbin.org", "Sec-Fetch-Dest": "document", 
      "Sec-Fetch-Mode": "navigate", 
      "Sec-Fetch-Site": "none", 
      "Upgrade-Insecure-Requests": "1", 
      "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", 
      "X-Amzn-Trace-Id": "Root=1-5eeef468-015d8f0c228367109234953c" 
   }, 
   "origin": "ip address", 
   "url": "https://httpbin.org/delay/1" 
}

fromEvent: 監聽 HTML 元素的事件,然後在監聽的事件觸發時發射事件及其屬性。

const clickEvent$ = fromEvent(document.getElementById('counter'), 'click');

Angular 在內部廣泛使用此概念來提供元件之間的資料傳輸和響應式表單。

訂閱過程

訂閱 Observable 非常簡單。每個 Observable 物件都將有一個用於訂閱過程的方法 subscribe。Observer 需要實現三個回撥函式來訂閱 Observable 物件。它們如下:

  • next: 接收並處理從 Observable 發射的值

  • error: 錯誤處理回撥

  • complete: 當 Observable 中的所有資料都發射完畢時呼叫的回撥函式。

定義完這三個回撥函式後,需要呼叫 Observable 的 subscribe 方法,如下所示:

const numbers$ = from([1,2,3,4,5,6,7,8,9,10]); 
// observer 
const observer = { 
   next: (num: number) => {      this.numbers.push(num); this.val1 += num }, 
      error: (err: any) => console.log(err), 
      complete: () => console.log("Observation completed") 
}; 
numbers$.subscribe(observer);

這裡,

  • next: 方法獲取發射的數字,然後將其推入區域性變數this.numbers中。

  • next: 方法還將數字新增到區域性變數this.val1中。

  • error: 方法只是將錯誤訊息寫入控制檯。

  • complete: 方法還將完成訊息寫入控制檯。

我們可以跳過errorcomplete方法,只編寫next方法,如下所示:

number$.subscribe((num: number) => { this.numbers.push(num); this.val1 += num; });

操作

Rxjs庫提供了一些運算子來處理資料流。一些重要的運算子如下:

filter: 使用回撥函式過濾資料流。

const filterFn = filter( (num : number) => num > 5 ); 
const filteredNumbers$ = filterFn(numbers$); 
filteredNumbers$.subscribe( (num : number) => { 
this.filteredNumbers.push(num); this.val2 += num } );

map: 使用回撥函式對映資料流並更改資料流本身。

const mapFn = map( (num : number) => num + num ); const mappedNumbers$ = mappedFn(numbers$);

pipe: 啟用兩個或多個運算子的組合。

const filterFn = filter( (num : number) => num > 5 ); 
const mapFn = map( (num : number) => num + num ); const processedNumbers$ = numbers$.pipe(filterFn, mapFn); 
processedNumbers$.subscribe( (num : number) => { this.processedNumbers.push(num); this.val3 += num } );

讓我們建立一個示例應用程式來嘗試本章中學習的反應式程式設計概念。

使用以下命令建立一個新的應用程式 reactive:

ng new reactive

將目錄更改為我們新建立的應用程式。

cd reactive

執行應用程式。

ng serve

更改 AppComponent 元件程式碼 (src/app/app.component.ts),如下所示:

import { Component, OnInit } from '@angular/core'; import { Observable, of, range, from, fromEvent } from 'rxjs'; 
import { ajax } from 'rxjs/ajax'; 
import { filter, map, catchError } from 'rxjs/operators'; 
@Component({ 
   selector: 'app-root', 
   templateUrl: './app.component.html', 
   styleUrls: ['./app.component.css'] 
}) 
export class AppComponent implements OnInit { 
   title = 'Reactive programming concept'; 
   numbers : number[] = []; 
   val1 : number = 0; 
   filteredNumbers : number[] = []; 
   val2 : number = 0; 
   processedNumbers : number[] = []; 
   val3 : number = 0; 
   apiMessage : string; 
   counter : number = 0; 
   ngOnInit() { 
      // Observable stream of data Observable<number>
      // const numbers$ = of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
      // const numbers$ = range(1,10); 
      const numbers$ = from([1,2,3,4,5,6,7,8,9,10]); 
      // observer 
      const observer = { 
         next: (num: number) => {this.numbers.push(num); this.val1 += num }, 
         error: (err: any) => console.log(err), 
         complete: () => console.log("Observation completed") 
      }; 
      numbers$.subscribe(observer); 
      const filterFn = filter( (num : number) => num > 5 ); 
      const filteredNumbers = filterFn(numbers$); 
      filteredNumbers.subscribe( (num : number) => {this.filteredNumbers.push(num); this.val2 += num } ); 
      const mapFn = map( (num : number) => num + num ); 
      const processedNumbers$ = numbers$.pipe(filterFn, mapFn); 
      processedNumbers$.subscribe( (num : number) => {this.processedNumbers.push(num); this.val3 += num } ); 
      const api$ = ajax({ 
         url: 'https://httpbin.org/delay/1', 
         method: 'POST', 
         headers: {'Content-Type': 'application/text' }, 
         body: "Hello" 
      }); 
      api$.subscribe(res => this.apiMessage = res.response.data ); 
      const clickEvent$ = fromEvent(document.getElementById('counter'), 'click'); 
      clickEvent$.subscribe( () => this.counter++ ); 
   } 
}

這裡,

  • 使用 of、range、from、ajax 和 fromEvent 方法建立 Observable。
  • 使用 filter、map 和 pipe 運算子方法處理資料流。
  • 回撥函式捕獲發射的資料,對其進行處理,然後將其儲存在元件的區域性變數中。

更改AppComponent模板(src/app/app.component.html),如下所示:

<h1>{{ title }}</h1> 
<div> 
   The summation of numbers ( <span *ngFor="let num of numbers"> {{ num }} </span> ) is {{ val1 }} 
</div> 
<div> 
   The summation of filtered numbers ( <span *ngFor="let num of filteredNumbers"> {{ num }} </span> ) is {{ val2 }} 
</div> 
<div> 
   The summation of processed numbers ( <span *ngFor="let num of processedNumbers"> {{ num }} </span> ) is {{ val3 }} 
</div> 
<div> 
   The response from the API is <em>{{ apiMessage }}</em> </div> 
<div> 
   <a id="counter" href="#">Click here</a> to increment the counter value. The current counter value is {{ counter }} 
<div>

這裡,

顯示了Observer回撥函式處理的所有區域性變數。

開啟瀏覽器,https://:4200。

Pipes

單擊點選此處連結五次。對於每個事件,事件將被髮射並轉發到Observer。Observer 回撥函式將被呼叫。回撥函式為每次點選遞增計數器,最終結果將如下所示:

Observer
廣告