JavaScript RxJS - Mitten im Stream einsteigen?

Hendoul

Commander
Registriert
Apr. 2008
Beiträge
2.156
Hi :)

Folgendes Szenario:
Bei einem Klick werden Daten geholt (fetchYearAndMonths()) - Simple Jahreszahlen und Monate, welche dann wiederum in einer For-Schleife im Gui angezeigt werden. Initial werden bei einigen Monaten darin enthaltene Daten geladen -> fetchDataForMonth() . Das sind also zwei separate calls.
Die Struktur im UI sieht dann so aus:

2022
--Januar
----datenpunkt
2021
--Dezember
----datenpunkt
--November
2020
--April
...

Der Code dazu sieht vereinfacht so aus:
Javascript:
ngOninit() {
        this.fetchYearAndMonths().pipe(
            tap( yearsAndMonths => {
                this.yearsAndMonths = yearsAndMonths;
            }),
            pluck('months'),
            filter( month => month.loadData === true),
            tap( monthsWithDataToBeLoaded => {
                month.data = fetchDataForMonth()
            })
        ).subscribe();
    }

Jetzt soll man mit einem Klick auf Monate die noch keine Daten anzeigen, welche holen können. Bei einem Klick auf 'April' im Jahr 2020 sollen nun also die entsprechenden Daten geholt werden. Dazu muss ja auch wieder fetchDataForMonth() aufgerufen werden.

Und meine Frage ist nun, wie ich das am elegantesten lösen kann mittels Observables. Kann ich bei dem bestehenden Code beim untersten tap irgendwie mich einklinken?

So wie es jetzt ist wird ja das ganze auch nur einmalig im ngOninit() getriggert. Ich muss aber via separatem Klick das fetchDataForMonth() nochmals auslösen können.

Klar könnte ich jetzt einfach in einem click Handler die Methode aufrufen und das dort separat handeln. Aber es muss doch eine elegantere Variante geben, bei der die Daten alle im selben Stream gehandhabt werden können?
 
Zuletzt bearbeitet:
du kannst ein Subject erstellen, bei dem du beim Button-Klick next() aufrufst. In ngOnInit hast du dann

Javascript:
this.clickSubject.pipe(
  startWith(undefined),
  switchMap(() => this.fetchYearAndMonths()),
  rest...
)

PS: hier musst du dann auf unsubscriben achten, da du sonst ein Memory-Leak hast. (takeUntil ist hier hilfreich).
 
Zuletzt bearbeitet:
ka, rxjs ist warscheinlich eine andere baustelle ;-) den rest hab ich mal wieder gelöscht.
 
Warum ein startWith(undefined) ?

So würde bei jedem click ja trotzdem noch fetchYearAndMonths() ausgeführt?
Bei einem Klick auf den Monat sollen aber nur die Daten für den geklickten Monat via fetchDataForMonth() geholt werden.
 
Sorry, da habe ich nicht aufmerksam genug gelesen.

Dann hier ein neuer Versuch. data$ ist ein Observable der immer ein Object mit allen aktuell vorhandenen Daten ausspuckt. fetchYearAndMonths holt die Basisdaten, fetchDataForMonth holt die Monatsdaten, addMonthDataToData nimmt die vorhandenen Daten und neuen Monatsdaten und erstellt daraus ein neues Datenobjekt.

Javascript:
this.data$ = from(fetchYearAndMonths()).pipe(
  switchMap((data) => monthClickSubject.pipe(
    map((month) => fetchDataForMonth(month)),
    mergeAll(),
    scan((combinedData, monthData) => addMonthDataToData(combinedData, monthData), data)
  ))
);

An der Oberfläche machst du dann <my-data-renderer *ngIf="(data$ | async) as data" [data]="data"></my-data-renderer>.
 
Zurück
Oben