Reputation: 1120
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.
I am always getting empty {}
in "data" in CustomSerializer.
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
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
Reputation: 1120
The following worked, credits: @timdeschryver
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
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