typescript - Den mest elegante måten å bytte abonnementer på i RxJS
Siden jeg allerede er en ganske erfaren utvikler i .NET, lærer jeg nye teknologier og jeg er nysgjerrig på hvordan du ville løst dette problemet i TypeScript + RxJS. La oss si at jeg har flere kolonner (felt) og jeg observerer deres verdiendringer. Jeg har også en bryter som har grupper av kolonner å observere - i dette eksemplet 1, 2, 3; A, B, C og X, Y, Z.
Hver gang jeg må bytte, vil jeg fjerne abonnementer fra den forrige gruppen, og opprette for den nye. Koden ser slik ut:
const log = (cid: string) => console.info(`${cid} changed`);
this.subscriptions.add(this.observeValueChanges("column 1").subscribe(log));
this.subscriptions.add(this.observeValueChanges("column 2").subscribe(log));
this.subscriptions.add(this.observeValueChanges("column 3").subscribe(log));
//Switching to another columns collection - I don't want previous subscriptions anymore
this.subscriptions.unsubscribe();
this.subscriptions.add(this.observeValueChanges("column A").subscribe(log));
this.subscriptions.add(this.observeValueChanges("column B").subscribe(log));
this.subscriptions.add(this.observeValueChanges("column C").subscribe(log));
//Switching to another columns collection - I don't want previous subscriptions anymore
this.subscriptions.unsubscribe();
this.subscriptions.add(this.observeValueChanges("column X").subscribe(log));
this.subscriptions.add(this.observeValueChanges("column Y").subscribe(log));
this.subscriptions.add(this.observeValueChanges("column Z").subscribe(log));
for referanse:
private subscriptions: Subscription = new Subscription();
og
observeValueChanges(columnId: string): Observable<any> {
return timer(0, 1000).pipe(map(_ => columnId), publish(), refCount());
}
Jeg kan få det til å fungere ved å erstatte subscription-objektet med et array av subscriptions, og etter bytte - kan jeg unsubscribe alle fra arrayet (og i stedet for å add - kan jeg push). Hva synes du? Jeg har ingen problemer med å lage «switch-signal» som observable.
Svar #1
Det er interessant at du brukte ordet "switch", fordi operatøren du leter etter kalles switchMap!
Hver gang en ny verdi mottas, vil den «mappe» den til en observabel og «bytte» fra den forrige kilden.
switchMap er en "Higher Order Mapping Operator", som gjør noen hyggelige ting for deg:
- mapper den innkommende verdien til en annen observerbar,
- abonnerer på det, så verdiene dens blir sendt ut
- håndterer automatisk avmelding fra disse «indre abonnementene»
Jeg har laget et fungerende eksempel i denne StackBlitz
import { fromEvent } from 'rxjs';
import { tap, map, switchMap } from 'rxjs/operators';
const group0 = document.querySelectorAll('.group0');
const group1 = document.querySelectorAll('.group1');
const selectedGroup = document.querySelectorAll('.selected-group');
// Array of possible sources to "switch" between
//
// [fromEvent] Creates an observable that emits DOM
// events for the specified elements.
//
// [map] Just maps the event to the target value.
const sources$ = [
fromEvent(group0, 'keyup').pipe(map(event => event.target['value'])),
fromEvent(group1, 'keyup').pipe(map(event => event.target['value']))
];
// selectedGroup$ emits the value of the selected radio button which
// for this example is conveniently the group's index in the array.
const selectedGroup$ = fromEvent(selectedGroup, 'change').pipe(
map(event => event.target['value'])
);
// source$ starts with the emission of selectedGroup$. Then we
// use [switchMap] to "switch" our source observable and map
// it to a new observable source. Then switchMap will
// automatically unsubscribe from the previous one.
const source$ = selectedGroup$.pipe(
switchMap(groupIndex => sources$[groupIndex])
)
// Notice we only need a single subscription!
source$.subscribe(console.log);
