Angular 2, traversing the same route and updating route data
Is there a way to get the router to go to the same route to update the route data by calling this canActivate jar 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 argument redirects to "/ create", which it should do for "/ create /: user" and "/ create /: user /: level", the guard is not called on the second time. Had a more complex routing scheme that got around this problem, but that would make it much easier.
Edit: One way to get around this is to add a dummy component, navigate to that, and then revert to "/ create", but this causes a noticeable transition and the component isn't reused.
this.router_.navigate(["/redirect"]).then((nav) => {
this.router_.navigate(["/create"]);
});
source to share
This is what I ended up with, since the "redirect" resulted in NavigationCancel events, I subscribed to Router.events and tracked if the last navigation had either a NavigationEnd or a 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;
});
}
};
source to share
canActivate
doesn't work for child routes.
You need to use canActivateChild
and install it on your router:
router
{
path: '/create',
loadChildren: 'yourModule#YourModule',
canLoad: [LevelGuard ],
canActivateChild: [LevelGuard ]
},
Guard
Just add to your LevelGuard
:
canActivateChild() {
// your code here
}
source to share