Jonathan Lightbringer
Jonathan Lightbringer

Reputation: 564

Ngxs - Promise on chains of actions?

When the application is initialize, I want to load user already (assuming there's a token in localStorage). I followed this guide, https://www.intertech.com/Blog/angular-4-tutorial-run-code-during-app-initialization/ but I'm lost right now in how would I incorporate it with ngxs, especially with my setup.

This function is called when the app is initialized:

  initializeApp() {
    const token = localStorage.getItem('token');
    if (token) {
      this.store.dispatch(new CheckSession(token));

   //hold the user
    }
  }

Action

@Action(CheckSession)
checkSession(sc: StateContext<SessionStateModel>, { payload }: CheckSession) {
sc.patchState({
  isLoading: true,
  token: payload
});

this.sessionService.checkSession()
  .subscribe(response => {
    sc.dispatch(new CheckSessionSuccess(response));
  }, error => {
    sc.dispatch(new CheckSessionFailed(error));
  });
}

I'm chaining the actions in the state, and I want my app to finish the entire chain of actions first before proceeding with the app.

Is this possible or am I doing this the wrong way?

Upvotes: 0

Views: 3171

Answers (2)

kctang
kctang

Reputation: 11202

Based on the async nature of code executions, consider this approach instead:

  • Start you app with a "Loading" route/page/component that just displays some kind of loading indicator.
  • Subscribe to ofActionSuccessful() of CheckSessionXxx that will change route/page/component accordingly.
  • Dispatch CheckSession. The outcome of this action will be handled by previous step.
  • If there is no token, change route/page/component accordingly.

The idea is avoid "waiting for process to complete" before rendering UI. Instead, always render UI based on current state (e.g. it is loading...). As async operations cause application state to change, update UI reactively.

This is not a coding solution but suggesting a design/thinking change - otherwise you will be trying to "wait" in many scenarios (IMHO, is like fighting the 'system'). Hope this helps.

Upvotes: 2

Sajeetharan
Sajeetharan

Reputation: 222657

I think you should implement this using AuthGuard,

@Injectable()
export class AuthGuard implements CanActivate {
    constructor(private _auth: AuthService, private _store: Store) {}

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        return this._isAuthenticated$.pipe(
            map(a => {
                if (a) {
                    return true;
                }
                this._store.dispatch(new CheckSession(state.url));
                return false;
            })
        );
    }

    @Select(AuthState.isAuthenticated)
    private _isAuthenticated$: Observable<boolean>;
}

and then use it before you actually call the component. You can check the following sample repository.

Upvotes: 0

Related Questions