How to access Resolver Data in ngrx/effects? (v7)

2019-09-04 15:37发布

问题:

I have api calls in the component which is time taking, so i have implemented resolver. I want the resolver data to be present in the store, for later use. I have implemented ngrx/store, ngrx/effects and ngrx/router-store.

Current state in component

  • getting data directly from resolver data in component (route is of type ActivatedRoute)

Desired state

To keep the resolver data in store, I need to access resolver data in effects. So i can simply dispatch action in component and subscribe the state.

  • dispatch action (LoadSubTopicDetails) in component
  • effects will listen to the action and access the same resolver data in effects

Problem

I am always getting empty {} in "data" in CustomSerializer.

  • using resolver in app-routing.module.ts

  • console output when routed to SubTopic/:subTopicId


How to access resolver data in effects? Thanks in advance.


Code

reducers / index.ts (CustomSerializer)

export interface RouterStateUrl {
  url: string;
  queryParams: Params;
  params: Params;
  data: any;
}

export const reducers: ActionReducerMap<State> = {
  routerReducer: fromRouter.routerReducer
};

@Injectable()
export class CustomSerializer implements fromRouter.RouterStateSerializer<RouterStateUrl> {
  serialize(routerState: RouterStateSnapshot): RouterStateUrl {
    let route = routerState.root;
    while (route.firstChild) {
      route = route.firstChild;
    }

    const { url, root: { queryParams } } = routerState;
    const { params, data } = route;
    console.log('routerData in CustomSerializer', { url, queryParams, params, data });

    return { url, queryParams, params, data };
  }
}

reducers / router.selector.ts

export const getRouterState = createFeatureSelector< RouterReducerState<RouterStateUrl> >('routerReducer');

component.ts

// current state of component
this.subTopicDetails = this.route.snapshot.data['content'];

// new way (desired state), getting "resolver data" from store
this.store.dispatch(new fromCourse.LoadSubTopicDetails);
this.store.select(getSubTopicDetails)
          .subscribe(data => this.subTopicDetails = data);

回答1:

Your Effects can't (and shoudn't) access the resolvers.

The resolvers should check if there is something in the store, and if there isn't it should dispatch an action to retrieve the data.

Todd Motto explains this very well with some good examples at https://toddmotto.com/preloading-ngrx-store-route-guards.



回答2:

The following worked, credits: @timdeschryver

  • Instead of effects, calling resolver-data, resolver needs to dispatch action.
  • the component will simply subscribe to the store and get updates

resolver

@Injectable()
export class SubTopicDetailsResolver implements Resolve<any> {
    constructor(private store: Store<State>) {}

    resolve(route: ActivatedRouteSnapshot) {
        this.store.dispatch(new fromCourse.LoadSubTopicDetails);
    }
}

effects

  @Effect()
  loadSubTopicDetails$ = this.actions$.pipe(
    ofType<fromCourse.LoadSubTopicDetails>(CourseActionTypes.LOAD_SUBTOPIC_DETAILS),
    withLatestFrom(
      this.store.select(getRouterState),
      (action, router) => router.state.params.subTopicId
    ),
    switchMap(subtopicId => {
      return this.service.getSubTopicDetails(subtopicId).pipe(
        map(data => new fromCourse.LoadSubTopicDetailsSuccess(data))
      );
    })
  );

component

this.store.select(getSubTopicDetails)
          .subscribe(data => {
            this.subTopicDetails = data;
          });