I currently have a resolver for a details page that checks to see if part of the store is available before moving forward to that page
Every time there is a router action (at the moment I'm trying them all)
ROUTER_REQUEST, ROUTER_NAVIGATED, ROUTER_NAVIGATION
There's a check to see if the store is loaded, if not, it consumes the appropriate end points and populates the store with the data
However, when I enter the app using a link to a details page (and not from the home page) the store loads after the resolver and the page just hangs.
So I would like to try to either; retry the the resolver function, a couple of times, or see if I can get the data to load before the resolver.
The load data if no store effect:
loadData$ = createEffect(() => this.actions$.pipe(
ofType<RouterNavigationAction>(ROUTER_REQUEST, ROUTER_NAVIGATED, ROUTER_NAVIGATION),
withLatestFrom(this.store.pipe(select(isLoaded))),
filter(([action, loaded]) => {
return !loaded;
}),
switchMap(() => {
return forkJoin([this.gamesService.getGames(), this.moviesService.getMovies(),
this.musicService.getMusic(40), this.comicsService.getComics(40)])
.pipe(
map((data) => {
const [games, movies, music, comics] = data;
return AppActions.loadDataComplete({ games, movies, music, comics });
})
);
})
));
This is a function, that is also router related thats executed long before the the one above.
setPageLoading$ = createEffect(() => this.actions$.pipe(
ofType<RouterNavigationAction>(ROUTER_REQUEST, ROUTER_NAVIGATED),
switchMap((action: Action) => {
// Returns true if request is ROUTER_REQUEST false if ROUTER_NAVIGATED
const loading = action.type.indexOf('request') !== -1;
return of(loading);
}),
map((loading) => {
return AppActions.setPageLoading({ pageLoading: loading });
}),
), { dispatch: true });
The resolver:
export class ComicDetailResolver implements Resolve<ComicDetail> {
constructor(private store: Store<State>) {
}
resolve(): Observable<ComicDetail> {
return this.store.pipe(select(getRouteID))
.pipe(
map((routeId: string) => this.store.dispatch(AppActions.getComicDetail({ routeId }))),
switchMap(() => this.store.pipe(select(getItem))),
filter((comic: ComicDetail) => {
console.log(comic, !!comic);
return !!comic;
}), first());
}
I tried using retry, but this doesn't work at all:
resolve(): any {
this.store.pipe(select(getRouteID))
.pipe(
map((routeId: string) => this.store.dispatch(AppActions.getComicDetail({ routeId }))),
switchMap(() => this.store.pipe(select(getItem))),
map((comic: ComicDetail) => {
let val = null;
if (!comic) {
val = throwError(of(null));
} else {
val = !!comic;
}
console.log(val);
return val;
}), retry(3)).subscribe(data => {
const loaded = !!data ? false : true;
console.log(loaded);
return loaded;
});
}
At the request of @Mikkel Christensen...
I have the app module, at the moment all of my effects are in the router effects:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { EffectsModule } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { reducers, metaReducers, CustomSerializer } from '@web/store/reducers';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AppEffects } from '@web/store/effects/main.effects';
import { HttpClientModule } from '@angular/common/http';
import { StoreRouterConnectingModule } from '@ngrx/router-store';
import { RouterEffects } from '@web/store/effects/router.effects';
import { LoadingComponent } from './shared/components/ui/loading/loading/loading.component';
import { HeaderComponent } from './shared/components/header/header.component';
import { LazyLoadImageModule } from 'ng-lazyload-image';
@NgModule({
declarations: [
AppComponent,
LoadingComponent,
HeaderComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
LazyLoadImageModule,
StoreModule.forRoot(reducers, { metaReducers }),
StoreDevtoolsModule.instrument({ maxAge: 25 }),
EffectsModule.forRoot([AppEffects, RouterEffects]),
StoreRouterConnectingModule.forRoot({serializer: CustomSerializer}),
],
providers: [{ provide: Window, useValue: window }],
bootstrap: [AppComponent]
})
export class AppModule { }
My component module is lazy loaded:
{
path: 'comics',
loadChildren: () => import('./features/comics/comics.module').then(m => m.ComicsModule)
},
Hope this is what you meant, if this isn't clear enough, or you need more info, please let me know