Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fork join two firebase observables

I am using angular2fire. I am querying and trying to get all the tours from a city.

getAllTours(cityId) {
    return this.af.database.list(`/cities/${cityId}/tours`)
        .map((tours): any => {
            tours.map((tour: any) => {
                tour.tour  = this.af.database.object(`/tours/${tour.$key}/tours`)
            });
            return tours;
        })
}

If i console.log the tour object, i get a array of "FirebaseObjectObservable".

I have to loop through all the FirebaseObjectObservable, to get the actual data.

I was wondering if i could forkJoin all the observables and get the output as an array with a single subscribe function.

Is this a right approach.

I know i can do an async pipe on all the observers array, but i would like to get the data inside the controller and then do some processing before its shown in the view, so async pipe is really not the best solution for me.

like image 431
harikrish Avatar asked Feb 06 '23 05:02

harikrish


1 Answers

Yes, forkJoin could be used to get the data for the inner observables:

getAllTours (cityId) {
    return this.af.database
        .list(`/cities/${cityId}/tours`)
        .mergeMap((tours) => {

            // The array of tours is going to be mapped to an observable,
            // so mergeMap is used.

            return Observable.forkJoin(

                // Map the tours to the array of observables that are to
                // be joined. Note that forkJoin requires the observables
                // to complete, so first is used.

                tours.map((tour) => this.af.database
                    .object(`/tours/${tour.$key}/tours`)
                    .first()
                ),

                // Use forkJoin's results selector to match up the result
                // values with the tours.

                (...values) => {
                    tours.forEach((tour, index) => { tour.tour = values[index]; });
                    return tours;
                }
            );
        });
}

Whether or not using forkJoin is the right approach will depend upon your requirements.

With the above code, the observable returned by getAllTours will not emit a value until all of the inner observables have completed - that is, until each of the city's tours have been looked up. That could affect perceived performance - if there is information in /cities/${cityId}/tours that could be shown before the information in /tours/${tour.$key}/tours is looked up, you won't be able to show it. Similarly, you won't be able to show the city's tours as the results arrive.

Using forkJoin makes dealing with the implementation simpler, but it might make the UI feel slower. (However, piecemeal updates to the UI might be something that you do not want.)

Note that if you do need to do some processing on each of the city's tours before it is shown in the view, you might be able to perform said processing on the observables in the code in your question. For example, using your getAllTours function:

observable = getAllTours(someCityId);
observable.map((tours) => {

    tours.forEach((tour) => {

        // With your function, tour.tour is an observable, so map
        // could be used to process the values.

        tour.tour = tour.tour.map((value) => {

            // Do some processing here with the value.
        })

        // And, if you are not interested in dynamic updates, you could
        // call first.

        .first();
    });
    return tours;
});

You could then use the async pipe in the template and it would receive your processed tours.

like image 131
cartant Avatar answered Feb 13 '23 06:02

cartant