Angular 中的可觀察物件
Observables in Angular
Angular 使用可觀察物件作為處理各種常用非同步操作的介面。比如:
Angular makes use of observables as an interface to handle a variety of common asynchronous operations. For example:
EventEmitter
類別派生自Observable
。You can define custom events that send observable output data from a child to a parent component.
HTTP 模組使用可觀察物件來處理 AJAX 請求和響應。
The HTTP module uses observables to handle AJAX requests and responses.
路由器和表單模組使用可觀察物件來監聽對使用者輸入事件的響應。
The Router and Forms modules use observables to listen for and respond to user-input events.
在元件之間傳遞資料
Transmitting data between components
Angular 提供了一個 EventEmitter
類別,它用來透過元件的 @Output()
裝飾器 傳送一些值。EventEmitter
擴充套件了 RxJS Subject
,並添加了一個 emit()
方法,這樣它就可以傳送任意值了。當你呼叫 emit()
時,就會把所傳送的值傳給訂閱上來的觀察者的 next()
方法。
Angular provides an EventEmitter
class that is used when publishing values from a component through the @Output()
decorator. EventEmitter
extends RxJS Subject
, adding an emit()
method so it can send arbitrary values. When you call emit()
, it passes the emitted value to the next()
method of any subscribed observer.
這種用法的例子參閱 EventEmitter 文件。下面這個範例元件監聽了 open
和 close
事件:
A good example of usage can be found in the EventEmitter documentation. Here is the example component that listens for open and close events:
<app-zippy (open)="onOpen($event)" (close)="onClose($event)"></app-zippy>
元件的定義如下:
Here is the component definition:
@Component({
selector: 'app-zippy',
template: `
<div class="zippy">
<div (click)="toggle()">Toggle</div>
<div [hidden]="!visible">
<ng-content></ng-content>
</div>
</div>
`,
})
export class ZippyComponent {
visible = true;
@Output() open = new EventEmitter<any>();
@Output() close = new EventEmitter<any>();
toggle() {
this.visible = !this.visible;
if (this.visible) {
this.open.emit(null);
} else {
this.close.emit(null);
}
}
}
HTTP
Angular 的 HttpClient
從 HTTP 方法呼叫中返回了可觀察物件。例如,http.get(‘/api’)
就會返回可觀察物件。相對於基於承諾(Promise)的 HTTP API,它有一系列優點:
Angular’s HttpClient
returns observables from HTTP method calls. For instance, http.get(‘/api’)
returns an observable. This provides several advantages over promise-based HTTP APIs:
可觀察物件不會修改伺服器的響應(和在承諾上串聯起來的
.then()
呼叫一樣)。反之,你可以使用一系列運算子來按需轉換這些值。Observables do not mutate the server response (as can occur through chained
.then()
calls on promises). Instead, you can use a series of operators to transform values as needed.HTTP 請求是可以透過
unsubscribe()
方法來取消的。HTTP requests are cancellable through the
unsubscribe()
method.請求可以進行配置,以獲取進度事件的變化。
Requests can be configured to get progress event updates.
失敗的請求很容易重試。
Failed requests can be retried easily.
Async 管道
Async pipe
AsyncPipe 會訂閱一個可觀察物件或承諾,並返回其發出的最後一個值。當發出新值時,該管道就會把這個元件標記為需要進行變更檢查的(譯註:因此可能導致重新整理介面)。
The AsyncPipe subscribes to an observable or promise and returns the latest value it has emitted. When a new value is emitted, the pipe marks the component to be checked for changes.
下面的例子把 time
這個可觀察物件繫結到了元件的檢視中。這個可觀察物件會不斷使用當前時間更新元件的檢視。
The following example binds the time
observable to the component's view. The observable continuously updates the view with the current time.
@Component({
selector: 'async-observable-pipe',
template: `<div><code>observable|async</code>:
Time: {{ time | async }}</div>`
})
export class AsyncObservablePipeComponent {
time = new Observable<string>(observer => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
路由器 (router)
Router
Router.events
以可觀察物件的形式提供了其事件。 你可以使用 RxJS 中的 filter()
運算子來找到感興趣的事件,並且訂閱它們,以便根據瀏覽過程中產生的事件序列作出決定。 例子如下:
Router.events
provides events as observables. You can use the filter()
operator from RxJS to look for events of interest, and subscribe to them in order to make decisions based on the sequence of events in the navigation process. Here's an example:
import { Router, NavigationStart } from '@angular/router';
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-routable',
template: 'Routable1Component template'
})
export class Routable1Component implements OnInit {
navStart: Observable<NavigationStart>;
constructor(router: Router) {
// Create a new Observable that publishes only the NavigationStart event
this.navStart = router.events.pipe(
filter(evt => evt instanceof NavigationStart)
) as Observable<NavigationStart>;
}
ngOnInit() {
this.navStart.subscribe(() => console.log('Navigation Started!'));
}
}
ActivatedRoute 是一個可注入的路由器服務,它使用可觀察物件來獲取關於路由路徑和路由引數的資訊。比如,ActivatedRoute.url
包含一個用於彙報路由路徑的可觀察物件。例子如下:
The ActivatedRoute is an injected router service that makes use of observables to get information about a route path and parameters. For example, ActivatedRoute.url
contains an observable that reports the route path or paths. Here's an example:
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-routable',
template: 'Routable2Component template'
})
export class Routable2Component implements OnInit {
constructor(private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.activatedRoute.url
.subscribe(url => console.log('The URL changed to: ' + url));
}
}
響應式表單 (reactive forms)
Reactive forms
響應式表單具有一些屬性,它們使用可觀察物件來監聽表單控制元件的值。 FormControl
的 valueChanges
屬性和 statusChanges
屬性包含了會發出變更事件的可觀察物件。訂閱可觀察的表單控制元件屬性是在元件類別中觸發應用邏輯的途徑之一。比如:
Reactive forms have properties that use observables to monitor form control values. The FormControl
properties valueChanges
and statusChanges
contain observables that raise change events. Subscribing to an observable form-control property is a way of triggering application logic within the component class. For example:
import { FormGroup } from '@angular/forms';
@Component({
selector: 'my-component',
template: 'MyComponent Template'
})
export class MyComponent implements OnInit {
nameChangeLog: string[] = [];
heroForm!: FormGroup;
ngOnInit() {
this.logNameChange();
}
logNameChange() {
const nameControl = this.heroForm.get('name');
nameControl?.valueChanges.forEach(
(value: string) => this.nameChangeLog.push(value)
);
}
}