angular - Generer Observable Array ved å bruke den originale Arrayen selv
Jeg har en tjeneste (MessageService) som sender ut noen meldinger basert på andre hendelser i appen.
Så har jeg en komponent MessagesComponent og jeg ønsker å vise alle meldingene som ble sendt ut av min tjeneste. Min tilnærming er å bruke en Observable<Message[]> for å lagre og reaktivt oppdatere UI hver gang en ny melding kommer. Her er min fungerende løsning
@Component({
selector: 'app-messages',
template: `
<h2>Received messages</h2>
<ol>
<li class="message" *ngFor="let message of messages$ | async">
{{ message }}
</li>
</ol>
`,
styles: [``]
})
export class MessagesComponent implements OnInit, OnDestroy {
destroy$ = new Subject<void>();
messages$ = new BehaviorSubject<Message[]>([]);
constructor(private messagesService: MessagesService) {}
ngOnInit(): void {
this.messagesService.message$
.pipe(
takeUntil(this.destroy$),
switchMap((message: Message) =>
this.messages$.pipe(
take(1),
map((all: Message[]) => {
return [...all, message];
})
)
)
)
.subscribe((messages: Message[]) => {
this.messages$.next(messages);
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
Akkurat nå starter jeg med en tom BehaviorSubject-array for å lagre alle meldingene. Deretter, i ngOnInit-hooken, abonnerer jeg på MessagesService's message$, og etter en ny utsendelse kombinerer jeg det jeg har i BehaviorSubject med den nye meldingen, slik at jeg sender ut en ny array med alle elementene. Løsningen min ser ut til å fungere, men jeg vil gjerne unngå abonnementet i NgOnInit (hvis mulig). Jeg tenkte på noe som dette
messages$: Observable<Message[]> = this.messagesService.message$.pipe(
switchMap((message: Message) =>
this.messages$.pipe(
startWith([]),
take(1),
map((all: Message[]) => [...all, message])
)
)
);
Jeg prøver å bruke selve arrayet som en del av sin egen initialisering ved å bruke SwitchMap + StartWith, men det ser ikke ut til å fungere, så jeg leter etter noen andre ideer :)
Å, meldingstypen er bare et enkelt grensesnitt med noen egenskaper.
export interface Message {
id: string;
value: string;
}
Svar #1
Du kan bruke scan-operatøren for å akkumulere tilstand. Den vil ha tilgang til forrige utslipp, og du kan bygge videre på det.
export class AppComponent {
messages$ = this.messagesService.message$.pipe(
scan((all, message) => all.concat(message), [])
);
constructor(private messagesService: MessagesService) { }
}
Når scan mottar en utslipp fra mesageService.message$, vil det kjøre funksjonen
(all, message) => all.concat(message)
og emittere resultatet. message er verdien mottatt fra tjenesten. Første gang har all en verdi av vår opprinnelige startverdi ([]). Etter det er det den tidligere emitterte verdien;
Merk at komponentkoden er sterkt forenklet; du trenger ikke: Subject, BehaviorSubject, abonnement, ngOnInit, ngOnDestroy, takeUntil, switchMap, eller take :-)
Her er en liten StackBlitz-demo.
