Raj
Raj

Reputation: 1120

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

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

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.

Problem

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


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);

Upvotes: 0

Views: 2516

Answers (3)

Domagoj Hamzic
Domagoj Hamzic

Reputation: 361

SOLUTION FOR Angular 15.2.8

If you're using NgRx State Management, a better solution will be creating a Guard which will return true when data is populated, and after, you can access them directly from the store (from the desired component). Resolvers are useless if you use data from State Management because resolved data will be stored in your State Management and you can access them using a reducer.

I'm using Angular 15.2.8.

@Injectable({
    providedIn: 'root'
})
export class CustomerHierarchyGuard implements CanActivate {

    constructor(private store: Store<IState>) {}

    getData(): Observable<any> {
        return this.store
            .select(getCustomerHierarchyModel).pipe(
                tap(data => {
                //Fetch new data, if there are no data
                    if (data == null) {
                        this.store.dispatch(CustomerHierarchyActions.loadCustomerHierarchy());
                    }
                }),
                //If data is empty, take will never trigger
                filter((data: any) => {
                    if (data == null) {
                        return false;
                    }
                    return true;
                }),
                take(1)
            )
    }

    canActivate(route: ActivatedRouteSnapshot): any {
        return this.getData().pipe(
            //Data is fetched
            switchMap(() => of(true)),
            catchError(() => of(false))
        )
    }
}

Upvotes: 0

Raj
Raj

Reputation: 1120

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-effects

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;
          });

Upvotes: 1

timdeschryver
timdeschryver

Reputation: 15507

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.

Upvotes: 6

Related Questions