Nate May
Nate May

Reputation: 4072

How to complete 2 http requests and then trigger another Action to merge the results

I have equivalent data types stored on 2 different databases.

Both types of data will be stored in my ngrx store Application State as well as a Merged State.

export interface ApplicationState {
   allSQLFoodData: FoodData;
   allUserFoodData: FoodData;
   mergedFoodData: FoodData;
}

What I need to do, is to get the allSQLFoodData and the allUserFoodData from the backends, store those value to the state and also merge both sets of data in the merged state.

I'm doing this by dispatch()ing an action INITIALIZE_DATA_ACTION which I then listen for in an effect service.

Here is where I stand in my effect service:

initialize-data-effect-service.ts

@Injectable()
export class InitializeDataEffectService {

   @Effect() initialize$: Observable<Action> = this.actions$
     .ofType(INITIALIZE_DATA_ACTION)
     .switchMap( (action) => {
      return Observable.combineLatest(
        this.foodService.loadSQLFoods( action.payload ),
        this.userDataService.loadUserData( action.payload )
      )
    })
    .switchMap(([allSQLFoodData, allUserData]) => {
      return Observable.of(
        new SQLFoodsLoadedAction( allSQLFoodData ),
        new UserDataLoadedAction( allUserData ),
        new BuildMergedFoodDataAction({sql: allSQLFoodData, user: allUserData})
      )
    })

  constructor( private actions$: Actions, private foodService: FoodService, private userDataService: UserDataService ) {

  }
}

As you can see, I am listening for the action of type INITIALIZE_DATA_ACTION and creating a combined observable using combineLatest() from the http responses. I then invoking the Action classes to set the Application state for each:

.switchMap(([allSQLFoodData, allUserData]) => {
  return Observable.of(
    new SQLFoodsLoadedAction( allSQLFoodData ),
    new UserDataLoadedAction( allUserData ),
    new BuildMergedFoodDataAction({sql: allSQLFoodData, user: allUserData})
  )
})

But this is giving the following error:

The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. Type argument candidate 'SQLFoodsLoadedAction' is not a valid type argument because it is not a supertype of candidate 'UserDataLoadedAction'. Types of property 'payload' are incompatible. Type 'AllUserData' is not assignable to type 'AllSQLFoodData'. Property 'foods' is missing in type 'AllUserData'.

I'm not sure how to

specifying the type arguments explicitly

and even if I did, I'm not sure how the Effect() decorator will now how to assign the results to the appropriate state values. how can I achieve this?

Upvotes: 1

Views: 231

Answers (1)

cartant
cartant

Reputation: 58430

To return an observable from switchMap that emits multiple actions from the effect, use Observable.from, which can take an array:

import 'rxjs/add/observable/from';
import 'rxjs/add/operator/concatMap';

...
.concatMap(([allSQLFoodData, allUserData]) => Observable.from([
  new SQLFoodsLoadedAction( allSQLFoodData ),
  new UserDataLoadedAction( allUserData ),
  new BuildMergedFoodDataAction({sql: allSQLFoodData, user: allUserData})
]))

Note that the switchMap is unecessary, as the returned observable will emit immediately. You can just use concatMap, instead.

Also, the effect doesn't assign anything to the state. Its only role is to emit actions. Those actions will be handled by reducers that will update the appropriate parts of the application's state.

Upvotes: 3

Related Questions