angular - Reintentar Resolver si los datos de ngrx no están disponibles
Actualmente tengo un resolvedor para una página de detalles que verifica si una parte de la tienda está disponible antes de continuar a esa página
Cada vez que hay una acción del router (por el momento los estoy probando todos)
ROUTER_REQUEST, ROUTER_NAVIGATED, ROUTER_NAVIGATION
Hay una verificación para ver si la tienda está cargada, si no, consume los puntos finales apropiados y llena la tienda con los datos
Sin embargo, cuando entro en la aplicación usando un enlace a una página de detalles (y no desde la página de inicio), la tienda se carga después del resolvedor y la página simplemente se queda colgada.
Así que me gustaría intentar o bien; reintentar la función del resolvedor, un par de veces, o ver si puedo hacer que los datos se carguen antes del resolvedor.
Los datos de carga si no hay efecto de almacenamiento:
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 });
})
);
})
));
Esta es una función, que también está relacionada con el enrutador y se ejecuta mucho antes que la de arriba.
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 });
El 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());
}
Intenté usar reintento, pero esto no funciona en absoluto:
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;
});
}
A petición de @Mikkel Christensen...
Tengo el módulo de la aplicación, en este momento todos mis efectos están en los efectos del enrutador:
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 { }
Mi módulo de componente se carga de forma perezosa:
{
path: 'comics',
loadChildren: () => import('./features/comics/comics.module').then(m => m.ComicsModule)
},
Espero que esto sea lo que querías decir, si esto no es lo suficientemente claro, o necesitas más información, por favor avísame
Respuesta #1
El problema, (aparte de mi comprensión de los resolutores) era que;
Estaba verificando los datos para la página de detalles, pero no verificando que los datos principales se hubieran cargado, los datos de los que se extraen los detalles.
Estaba intentando encontrar una manera de reintentar o hacer que el resolvedor esperara, pero una vez que lo hice verificar los datos principales, de todos modos esperó (por lo tanto, mi comprensión de los resolvedores ha mejorado)
Además, la razón por la que los datos principales tardan un poco en cargar es porque consume 4 puntos finales diferentes, lo que por sí solo causa condiciones de competencia que necesitaban ser examinadas.
El código actualizado:
resolve(): Observable<ComicDetail> {
// Added this
return this.store.pipe(select(isLoaded))
.pipe(
filter((loaded: boolean) => {
return !!loaded;
}),
// Added above
switchMap(() => 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());
}
