6

I was trying to restrict the user from navigating away from the current page by using CanDeactivate (if form is dirty and not saved). By the time we click on any link, Router_Navigation event is getting called and it is updating the router state in store and if I cancel the page navigation on modal pop up (from can deactivate), Router_Cancel event is being called, but the current router state is not getting updated (it’s still pointing to other page).

I saw this in ngrx documentation:

ROUTER_CANCEL and ROUTER_ERROR contain the store state before the navigation. Use the previous state to restore the consistency of the store.

Can someone please help me on how to get previous state from Router_cancel Action.

Thanks

kumar
  • 248
  • 3
  • 12

2 Answers2

5

I solved this by creating an applicationRouter state to maintain current and previous routes, whenever ngrx router dispatches a ROUTER_NAVIGATION Event i am listening to it and updating my applicationRouterState.

At each point applicationRouter will have only two router events (current and previous state).

and whenever Router_Cancel is triggered i am toggling the previous router and current router state.

PFB, the soln:

@Effect()
    navigationListener$ = this.actions$.ofType('ROUTER_NAVIGATION')
        .pipe(
            switchMap((routerNavigationAction: RouterNavigationAction<RouterDefinition>) => {
                return of(routerNavigationAction).pipe(
                    withLatestFrom(this._routerNavigationData$),
                    map(([action, routerNavData]: [RouterNavigationAction<RouterDefinition>, RouterState]) => {
                        // TODO: Move this logic to Reducer
                        if (!(routerNavData.currentRouter && routerNavData.currentRouter.url
                            && routerNavData.previousRouter && routerNavData.previousRouter.url)) {
                            routerNavData.previousRouter = routerNavData.currentRouter =  action.payload.routerState;
                        } else {
                            routerNavData.previousRouter = routerNavData.currentRouter;
                            routerNavData.currentRouter = action.payload.routerState;
                        }
                        return new fromActions.MaintainPrevCurrRouterStateAction(routerNavData);
                    })
                );
            })
        );

And this is my state object:

export interface RouterDefinition {
    url: string;
    queryParams: Params;
    params: Params;
    segments: string[];
}
export interface RouterState {
    currentRouter: RouterDefinition;
    previousRouter: RouterDefinition;
}
kumar
  • 248
  • 3
  • 12
3

I use a ngrx/effect to store the latest two ROUTER_NAVIGATION actions, and re-dispatch the previous one when I get a ROUTER_CANCEL or ROUTER_ERROR so that the router state is completely restored.

@Injectable()
export class RouterEffects {
    private previousRouterNavigationAction: RouterNavigationAction<
        RouterStateUrl
    >;
    private currentRouterNavigationAction: RouterNavigationAction<
        RouterStateUrl
    >;

    @Effect({ dispatch: false })
    save$: Observable<Action> = this.actions$.pipe(
        ofType(ROUTER_NAVIGATION),
        switchMap((action: RouterNavigationAction<RouterStateUrl>) => {
            this.previousRouterNavigationAction = this.currentRouterNavigationAction;
            this.currentRouterNavigationAction = { ...action };
            return Observable.empty();
        })
    );

    @Effect()
    load$: Observable<Action> = this.actions$.pipe(
        ofType(ROUTER_CANCEL, ROUTER_ERROR),
        switchMap(action => Observable.of(this.previousRouterNavigationAction))
    );

    constructor(private actions$: Actions) {}
}
Israfel
  • 1,652
  • 1
  • 12
  • 6
  • This is a hack solution because when I re-dispatch the previous `ROUTER_NAVIGATION` action from this effect, there isn't an actual navigation. – Israfel May 23 '18 at 07:40
  • Hack or not, it does work and I found it easier to understand than other solutions. I can't believe ngrx doesn't do this for you. Completely goes against what you would expect. – Josh Werts Aug 02 '18 at 15:38
  • That is great, you def. saved my day – JSON Aug 21 '18 at 21:31