Jarosław Rewers
Jarosław Rewers

Reputation: 1089

wait for state in resolver (ngrx)

right now I have a resolver calling standard service and returning an Observable:

return this.someService.getData()
      .map(data=> data.json())

I want to replace it with ngrx effects and store. When resolver is run I want to dispatch requestAction intercepted by effect, which makes http request. Once data is returned I dispatch dataReceivedAction with data as payload.

Basically i want to dispatch an action in resolver and wait till new data is in part of store. I try to do it this way:

return Observable.combineLatest(
      Observable.of(
        this.store.dispatch(new requestAction())
      ),
      this.store.select(store => store.dataIWaitFor),
      (dispatchedAction, dataArrived) => dataArrived
    )
      .skip(1)

it's not working, I mean the resolver doesn't render the component but when at the end of each of these returned Observables I add

.do(data => console.log(data))

the same data is logged. What I do wrong?

Upvotes: 3

Views: 3679

Answers (2)

Thomas Perrin
Thomas Perrin

Reputation: 784

I tried to implement your solution in a resolver instead of a guard, it populates well my ngrx store, but the navigation is not done, I am stuck on the page where I call the route with resolver from. Also in the store we can see the data and router-store/requested and router-store/navigation but not router-store/navigated, neither router-store/cancel like when a guard blocks the navigation. Do you know what I miss and why nothing happends on the navigation side? I used the resolver in my route this way:

  {
    path: "params/users",
    canActivate: [IsAdminGuard],
    component: UsersComponent,
    resolve: { usersLoaded: UsersResolver }
  }

And here is the resolver, almost like your guard:

@Injectable()
export class UsersResolver implements Resolve<boolean> {

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

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> {        
  // API calls with ngrx effect 
  this.store.dispatch(usersActions.GET_USERS_TRY());

  // Select users in store, switchMap(true) if array not empty
  return this.store.pipe(
    select(UsersSelectors.allUsers),
    filter((allUsers: users[]) => allUsers.length > 0),
    switchMap((allUsers: users[]) => return of(true))
  );
  }
}

Upvotes: 0

alexKhymenko
alexKhymenko

Reputation: 5598

You can do something like this

@Injectable()
export class SomeGuard implements CanActivate {
constructor(private store: Store<AppState>) {}

waitForDataToLoad(): Observable<Something> {
    return this.store.select(state => state.something)
        .filter(something => something && !something.empty);
}

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    this.store.dispatch({type: LOAD_something});

    return this.waitForDataToLoad()
        .switchMap(() => {
            return Observable.of(true);
        });
    }
}
}

Upvotes: 2

Related Questions