Angular 8 反应式编程


反应式编程是一种处理数据流和变化传播的编程范式。数据流可以是静态的或动态的。静态数据流的一个示例是数组或数据集合。它将有一个初始数量并且不会改变。动态数据流的一个例子是事件发射器。每当事件发生时,事件发射器都会发出数据。最初,可能没有事件,但随着时间的推移,事件会发生并且会被发出。

反应式编程使数据流能够从一个名为的源发出 可观察的 并且发出的数据流将被称为的其他源捕获 Observer 通过称为订阅的过程。这个 Observable/Observer 模式还是简单的 Observer 模式极大地简化了编程上下文中复杂的更改检测和必要的更新。

JavaScript 没有对反应式编程的内置支持。 RxJs 是一个 JavaScript 库,可以在 JavaScript 中实现反应式编程。角度用途 RxJs 图书馆广泛做以下提到的高级概念:

  • 组件之间的数据传输。
  • HTTP 客户端。
  • Router.
  • 反应形式。

让我们学习响应式编程 RxJs 本章中的图书馆。

可观察的


如前所述, 可观察的 是数据源,它们可能是静态的或动态的。 Rxjs 提供了很多创建方法 可观察的 来自常见的 JavaScript 对象。让我们看看一些常用的方法。

of: 在一个序列中发出任意数量的值,最后发出一个完整的通知。

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

Here,

  • numbers$ is an 可观察的 对象,订阅后将依次发出 1 到 10 个。

  • 美元符号 ($) 在变量的末尾是标识该变量是可观察的。

range : 依次发出一个范围的数字。

const numbers$ = range(1,10)

from : 发射数组、promise 或 iterable。

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',方法:'POST',标题:{'Content-Type':'application/text'},正文:“Hello”});

Here,

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"
}

从事件 : 监听一个 HTML 元素的事件,然后在监听的事件触发时发出该事件及其属性。

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

Angular 在内部广泛使用该概念来提供组件之间的数据传输以及用于响应式表单。

订阅流程


订阅 Observable 非常简单。每个 Observable 对象都会有一个方法,用于订阅订阅过程。 Observer 需要实现三个回调函数来订阅 Observable 对象。它们如下:

  • next : 接收并处理 Observable 发出的值

  • error : 错误处理回调

  • complete : Observable 的所有数据都发出时调用的回调函数。

一旦定义了三个回调函数,就必须调用 Observable 的 subscribe 方法,如下所示:

const numbers$ = from([1,2,3,4,5,6,7,8,9,10]); 
// 观察者
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);

Here,

  • next :方法获取发射的数字,然后将其压入局部变量, this.numbers .

  • next : 方法也将数字添加到局部变量中, 这个.val1 .

  • error : 方法只是将错误信息写入控制台。

  • complete :方法还将完成消息写入控制台。

我们可以跳过 error and complete 方法并且只写 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 } );

让我们创建一个示例应用程序来尝试本章学习的反应编程概念。

使用以下命令创建一个新的应用程序,响应式:

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
        // 常量数字$ = of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 常量数字$ = range(1,10);
        const numbers$ = from([1,2,3,4,5,6,7,8,9,10]);
        // 观察者
        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++ );
    }
}

Here,

  • 使用 of、range、from、ajax 和 fromEvent 方法来创建 Observable。
  • 使用过滤器、映射和管道运算符方法来处理数据流。
  • 回调函数捕获发出的数据,对其进行处理,然后将其存储在组件的局部变量中。

更改 应用组件 template (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>

Here,

显示所有被处理的局部变量 Observer 回调函数。

打开浏览器,http://localhost:4200。

Pipes

点击 点击这里 链接五次。对于每个事件,该事件将被发出并转发到 Observer .将调用观察者回调函数。回调函数每点击一次,计数器递增一次,最终结果如下图:

Observer