3
votes

Is there a way to force the router to navigate to the same route, in order to update route data by calling this canActivate guard twice:

@Injectable()
export class LevelGuard implements CanActivate {

    private user_query_: string;
    private level_query_: string;

    constructor(private router_: Router, private query_resolver_: QueryResolver) { };

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {

        let user: string = route.params["user"];
        let level: string = route.params["level"];

        if (!user && !level) { //=> "/create"
            this.query_resolver_.setQuery({ user: this.user_query_ || "none", level: this.level_query_ || "none" });
            this.level_query_ = "";
            this.user_query_ = "";
            return true;
        }
        else if (user && !level) { //=> "/create/:user"
            this.router_.navigate(["/create"]);
            this.level_query_ = "none";
            this.user_query_ = user;
            return false;
        }
        else { //=> "/create/:user/:level"
            this.router_.navigate(["/create"]);
            this.level_query_ = level;
            this.user_query_ = user;
            return false;
        }
    };
}

If the route is already "/create", then when this canActivate guard redirects to "/create", which it should do for "/create/:user" and "/create/:user/:level", the guard is not called a second time. Had a more complicated routing scheme that circumvented this problem but this would streamline it greatly.

Edit: One way to get around this is to add in a dummy component, navigate to that then back to "/create", but it causes a noticeable transition and component is not reused.

this.router_.navigate(["/redirect"]).then((nav) => {
    this.router_.navigate(["/create"]);
});
2

2 Answers

0
votes

canActivate wont work for child routes.

You need to use canActivateChild and set it on your router:

Router

{
    path: '/create',
    loadChildren: 'yourModule#YourModule',
    canLoad: [LevelGuard ],
    canActivateChild: [LevelGuard ]
  },

Guard

Just add in your LevelGuard:

canActivateChild() {
    // your code here
}
0
votes

This is what I ended up doing, since the "redirects" resulted in NavigationCancel events, I subscribed to Router.events and kept track of whether the last navigation had either a NavigationEnd or NavigationCancel. Tried with a boolean flag, but couldn't figure out a way to reset the flag to false at the right time.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {

    let mode = route.url[0].path as "create" | "play";

    let user: string = route.params["user"];
    let level: string = route.params["level"];

    if (!user && !level) { //=> "/mode"

        if (this.last_navigation_outcome instanceof NavigationCancel) {
            return Observable.of(true);
        }
        else {
            user = this.user_identity_.id;
            let query = { mode, user, level: "none" };              
            this.query_resolver_.setQuery(query);
            return this.level_resolver_.resolveLevel(query).map((level) => {
                return true;
            });
        }
    }
    else if (user && !level) { //=> "/mode/:user"
        let query = { mode, user, level: "none" }; 
        this.query_resolver_.setQuery(query);
        return this.level_resolver_.resolveLevel(query).map((level) => {
            this.router_.navigate(["/" + mode]);
            return false;
        });
    }
    else { //=> "/mode/:user/:level"
        let query = { mode, user, level };
        this.query_resolver_.setQuery(query);
        return this.level_resolver_.resolveLevel(query).map((level) => {
            this.router_.navigate(["/" + mode]);
            return false;
        });
    }
};