Skip to content

canActivate=>false can result in blank screen/dead link #16211

Description

@matthewerwin

I'm submitting a ... (check one with "x")

[ X ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

canActivate, canActivateChild will result in NavigationCancel event if 'false'/reject() happens in the Guard. Additionally the URL is reset to the base path before the navigation attempt (as though to leave the user on the page they navigated from).

This current behavior means that if you just go directly to a location in your browser (e.g. localhost:4200/some-guarded-route) and it returns false the URL will read "localhost:4200/" with a blank page as navigation is canceled.

Expected behavior

The right behavior to me seems that the router should try to see if there are further matches in the routing table (i.e. the guard 'false' case would cover the use case scenario of #14515). By continuing down the route table the behavior would be to land on '**' route in the worst case while also enabling conditional routing.

Minimally NavigationCancel event should determine if the resetUrlToCurrentUrlTree is an already displayed route (either rendering it if necessary or doing nothing if it's already rendered).

I will see if I can issue a PR to fix this for review. Will need to determine what the side effects would be for cases where people would expect just to stay on the currently rendered page (considering it was already rendered). However, to me I think the correct behavior for a dead link should be to go down the route table and hit '**' for a 404 or something vs doing nothing when the user clicks on it and rendering nothing if the user hits the route directly before any route has been rendered.

Minimal reproduction of the problem with instructions

Plunker won't work for this because you would need to be able to hit the route as though it was bookmarked to reproduce.

Effectively just create project using CLI, create a route guard, return false in the methods. Then attempt to hit the route directly without loading a root non-guarded route first.

What is the motivation / use case for changing the behavior?

Motivation is that you won't have the user on a blank screen if they happen to hit a guarded route without meeting the conditions.

Second part is that by resolving these scenarios by continuing down the route table you would enable conditional routing for scenarios such as:

Creating routes like:
localhost:4200/:organization
localhost:4200/:user

If the route guard found a valid organization it could allow the request to move forward vs user. This is all gravy. A start is to not give the user a blank page when resetting the route via resetUrlToCurrentUrlTree when the current Url wasn't rendered in the first place.

Please tell us about your environment:

Mac OS Sierra (10.12.4), WebStorm, NPM, ng serve

  • Angular version: Angular 4.0.2
  • Browser: Chrome 57
  • Language: TypeScript 2.2.2

  • Node (for AoT issues): node --version =
    Node 7.9

Activity

  1. gedclack commented on Apr 24, 2017

    @gedclack

    Yep, this also happens to me.
    for temporary solution, I added router.navigate(['somewhere']) before returning false

  2. xdom commented on Jun 6, 2017

    @xdom

    @matthewerwin How would you handle a use-case when you actually need to cancel routing after canActivate returned false? I agree with your proposal but it would be good if both "modes" could be preserved somehow, wouldn't it? E.g. (only my quick idea) return a Promise which:

    • resolve(true) when canActivate === true
    • resolve(false) when canActivate === false and should proceed (find next suitable route)
    • reject() when canActivate === false and routing should be cancelled.
  3. matthewerwin commented on Jun 6, 2017

    @matthewerwin
    Author

    This also resolves #14515

  4. andrui commented on Sep 20, 2017

    @andrui

    Issue seems is related to #18975

  5. code-tree commented on Oct 5, 2017

    @code-tree
    Contributor

    I'm surprised more people haven't come across this yet... Seems to be a pretty big flaw if there's no decent solution.

    Is it possible to detect if this is the first route being navigated to (i.e. on page load)? Could then redirect if so, else simply return false.

  6. code-tree commented on Oct 5, 2017

    @code-tree
    Contributor

    This seems to be working for me:

    if (this.router.routerState.snapshot.url === '')
        this.router.navigate(['/'])

    I assume url will only ever be an empty string on first load, as index gives value '/'

  7. AngularTx commented on Oct 10, 2017

    @AngularTx

    Hi ,

    I too have the same issue . When canactivate returns false, the route doesn't stay with the previous one, instead goes blank ..like

    http://localhost:1000/a1
    when i try to navigate to a2 - canactivate is triggered, which returns false
    and i get http:/localhost:1000 - blank .

    I my case i dont want to return to the "/" . I just need to stay on the last route navigated . ie http://localhost:1000/a1(with no refresh)

  8. montella1507 commented on Oct 10, 2017

    @montella1507

    I really didn't get why is the return type of canActivate boolean, observable

    and not - for example boolean | RouterCommands or observable<boolean | routercommands>

    so you would just:
    if (...) { return true; } return ["/"];
    to force you not to forget on new router commands..

  9. AngularTx commented on Oct 11, 2017

    @AngularTx

    any updates here? i am using the latest version of angular 4.

  10. matthewerwin commented on Oct 11, 2017

    @matthewerwin
    Author

    @jasonaden what's the word? What do you need from me?

    Anyone else on this thread--if you need a build for a particular version of Angular let me know and I'll get it posted to MyGet feed so you can pull in.

  11. AngularTx commented on Oct 16, 2017

    @AngularTx

    Hi ,

    Any solution for this issue ? or will this be taken care in the next updates?

    The issues comes in when the user changes the url from the browser url and tries navigating to a page which he is not allowed to , because of the canactivate condition returning false;

    If any alternative , please suggest.

  12. Goodwine commented on Nov 30, 2017

    @Goodwine

    It would be great if we could return boolean|string|Promise<boolean|string>|Observable<boolean|string> where if the value is a boolean then the current behaviour of bouncing the request would be kept, however if we return a string, then the router should navigate to that route relative to the attempted-to-activate route.

    for example:
    if I'm trying to navigate to some/place/123 and I return .., I should go to some/place,
    but if I return ../bad then I should go to some/place/bad

    Or... some other behavior similar to that.

  13. matthewerwin commented on Nov 30, 2017

    @matthewerwin
    Author

    @Goodwine that should be supported within my version. Please feel free to verify by installing and checking you use case -- returning false on all routes will result in dead link, reject or observable error results in error, and support for ** default route:

    npm uninstall @angular/router
    npm install @angular/router --registry https://myget.org/F/snaptech/npm

  14. matthewerwin commented on Nov 30, 2017

    @matthewerwin
    Author

    @Goodwine sorry, sent that too soon. The idea of having strings doesn't make a lot of sense to me. CanActivate guards are sentinel, not dynamic route builders. if you want that to be the fallback you would add next in the route table and if it needed conditionality just create yet another CanActivate.

    In essence what you're asking for is supported behavior in my code but not in the way you're trying to do it which would muddy domains of responsibility

  15. 81 remaining items

  16. tharindu-nilakshana commented on Jun 23, 2022

    @tharindu-nilakshana

    i added these in app module file

    import { JwtHelperService,JWT_OPTIONS } from '@auth0/angular-jwt';
    
     providers: [
        { provide: JWT_OPTIONS, useValue: JWT_OPTIONS },
        AuthGuard,
        JwtHelperService,
      ],
    

    this solved my blank screen problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions