简体   繁体   中英

Rxjs Combine dynamic amount of observables

I am creating a loader for @ngx-translate , that loads multiple JSON translation files from a directory based on the selected language.

The way I am currently loading the files is via an index.json file that contains an array with names and extensions for the translation files for that language.

The structure of the translation files is as follows:

- assets
  - i18n
    - index.json <-- Contains an array of translation file names
    - en_US
      - home.json
      - header.json
    - de_DE
      - home.json
      - header.json

An example of the index.json is as follows:

[
  "home.json",
  "header.json"
]

Since the Angular HttpClientModule cannot load the contents of a directory or the names of files in a directory (it can only load single json files) I need to define the names in the index.json .

This also means I have to first load the index.json and then load all the other files in the directory.

In code this means the following:

  1. Load the index.json
  2. Loop over the array of file names
  3. Load every file in a separate request
  4. When all finished combine all the file contents into one object

What I've tried

 public getTranslation(language: string): Observable<any> {
    return this.getIndexFile(language)
      .pipe(
        map((fileNames: string[]) => {
          const promises = [];

          for (const name of fileNames) {
            const path = Translation.replaceUrlPlaceholder(this.path, language);
            promises.push(this.http.get(path + '/' + name + '.json').toPromise());
          }

          return Promise.all(promises);
        }),
      );
  }

So I've tried this with promises, but this obviously doesn't work (since an observable must be returned). Also the solution described here doesn't work for me, since I need to dynamicatlly create an unlimited amount of observables and wait for them to finish before I can start on step 4. (combine all files).

What should be the correct way to do this?


  • Angular 7.1.0
  • RxJS 6.3.3
  • @ngx-translate/core 11.0.1

Update

Stackblitz here: Combining observables

So after searching and trying some more I found the solution to my answer. There where 2 problems:

1. Flatmap vs Map

I was using a map() instead of a flatMap . The difference is that the flatMap will execute when the first observable has finished. This way the subscribe won't get the result until the flatMap observable has finished.

2. Promise.all vs forkJoin

The observable equivalent of Promise.all() is forkJoin() . ForkJoin wil execute all observables in parallel and returns the result of all observables in one array.


The result

So updating the code above will result in the following stackblitz: solution

Or in code:

  public getTranslation(language: string): Observable<any> {
    return this.getIndexFile(language)
      .pipe(
        flatMap((fileNames: string[]) => {
          const observables: Observable<any>[] = [];

          for (const name of fileNames) {
            const path = 'assets/i18n/' + language + '/' + name + '.json';
            observables.push(this.http.get(path));
          }

          // Run all the observables in parallel
          return forkJoin(observables);
        }),
      );

Separation of Concern

My code contains multiple actions in one function which make it hard to test. So that should be separated. @trichetriche made a version which includes separation of concern.

See his Stackblitz for the code: Stackblitz

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM