I'm having issues with the Angular router during the process of navigating to a provided state.
I have tried using a custom guard with canLoad()
and canActivate()
functions returning booleans as true
without any luck.
The Angular docs states the following:
NavigationCancel: An event triggered when navigation is canceled. This is due to a Route Guard returning false during navigation.
As I dont get any more debugging information from the Router tracing (no reason is given) I am forced to check here if there is a fix, or if this is an existing bug. I also appreciate any information regarding other means of debugging the router in Angular 6.
Console output
I've created a small project in here. The project requires access to a provider, I'm using the the provided OpenID Connect provider given in the repository of angular-oauth2-oidc. Password/username is max/geheim.
How to reproduce the error
UPDATE
I suspect it has something to do with navigating to children: []
routes.
I did a little bit of debugging in your code, and I have found that is not because of links, auth guard, navigation declaration or module configuration nor router beeing flakey in AG6, but it is because of..... OAuth lib you are using.
But let me explain. I found out that following scenario is happening:
false
is propagated all over whole pipes and subscription in routerboolean
(not even false, but bool) as a value in pipe results in CANCEL navigation without any reason.Ill add some code references. But in general, that is what happening. Your oauth lib modifies url during navigation and that causes it to be canceled. Guards had like nothing to do with that in straight forward way.
So in general - it is not canceled because "access is denied" like in case of guards, but it is canceled because new navigation will have to be performed, so it is short-circuited by cancel.
Here is (not all) related code:
OAuth lib modifying
if (!this.oidc) {
this.eventsSubject.next(new OAuthSuccessEvent('token_received'));
if (this.clearHashAfterLogin && !options.preventClearHashAfterLogin) {
location.hash = '';
}
return Promise.resolve();
}
Nav triggering on url change:
Router.prototype.setUpLocationChangeListener = function () {
var _this = this;
// Don't need to use Zone.wrap any more, because zone.js
// already patch onPopState, so location change callback will
// run into ngZone
if (!this.locationSubscription) {
this.locationSubscription = this.location.subscribe(function (change) {
var rawUrlTree = _this.parseUrl(change['url']);
var source = change['type'] === 'popstate' ? 'popstate' : 'hashchange';
if(this.rou)
var state = change.state && change.state.navigationId ?
{ navigationId: change.state.navigationId } :
null;
setTimeout(function () { console.error("FROM LOCATION SUB");_this.scheduleNavigation(rawUrlTree, source, state, { replaceUrl: true }); }, 0);
});
}
};
Nav id modification - happens right away:
var id = ++this.navigationId;
console.error("ANOTHER SCHEDULED LOL LOL LOL!!!");
this.navigations.next({ id: id, source: source, state: state, rawUrl: rawUrl, extras: extras, resolve: resolve, reject: reject, promise: promise });
// Make sure that the error is propagated even though `processNavigations` catch
// handler does not rethrow
return promise.catch(function (e) { return Promise.reject(e); });
That is what is passed to router to start "async" routing - id is nav id (incremented previously)
Router.prototype.runNavigate = function (url, rawUrl, skipLocationChange, replaceUrl, id, precreatedState) {
This check (it in runNav) fails as first as id changed so 2!==3 - FALSE is returned to the pipe
var preactivationCheckGuards$ = preactivationSetup$.pipe(mergeMap(function (p) {
if (typeof p === 'boolean' || _this.navigationId !== id) //NAVIGATION ID CHANGES HERE!
{
console.warn("PREACTIVATE GUARD CHECK ");
console.log(p);
// debugger;
return of(false);
}
there are couple of more subscriptions in chaing, all of them have some piped mappings etc as well as known condition check.
var preactivationResolveData$ = preactivationCheckGuards$.pipe(mergeMap(function (p) {
if (typeof p === 'boolean' || _this.navigationId !== id)
return of(false);
Notice that is what I wrote earlier, that if you get ANY booean here, false is pushed forward. Since we have false
here already beacause check failed in previous pipe-map....
Finally at the end of the chain
if (typeof p === 'boolean' || !p.shouldActivate || id !== _this.navigationId || !p.state) {
// debugger;
navigationIsSuccessful = false;
return;
}
result flag set to false and this results in
.then(function () {
if (navigationIsSuccessful) {
_this.navigated = true;
_this.lastSuccessfulId = id;
_this.events
.next(new NavigationEnd(id, _this.serializeUrl(url), _this.serializeUrl(_this.currentUrlTree)));
resolvePromise(true);
}
else {
_this.resetUrlToCurrentUrlTree();
_this.events
.next(new NavigationCancel(id, _this.serializeUrl(url), ''));
resolvePromise(false);
}
NavigationCancel
without any message (last param is the message - emtpy string here) that you know well :).
It took me much more than it should as I didn't know angular internals + those bloody pipes... pipes everywhere.
as for docs
NavigationCancel: An event triggered when navigation is canceled. This is due to a Route Guard returning false during navigation.
well they forgot to mention that internally router can cancel navigation is there is queue of navigaations building up :)
Cheers!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With