angular: canLoad guard does not subscribe to Observable
I’m submitting a…
[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior
When passing an Observable to the canLoad
guard for a lazyly loaded module routerLink
stops navigating to any route even if the boolean in Observable<boolean>
is true
. No error messages in console.
app-routing.module:
const routes: Routes = [
{
path: '',
component: SomeAComponent,
pathMatch: 'full'
},
{
path: 'someb',
component: SomeBComponent,
canActivate: [
AuthGuard
],
},
{
path: 'lazy',
loadChildren: './lazy/lazy.module#LazyModule',
canLoad: [
AuthGuard
]
},
{
path: '**',
redirectTo: '/'
}
];
auth.guard:
@Injectable()
export class AuthGuard implements CanLoad, CanActivate {
/*
* @angular-redux/store
* false --> next() --> true --> ...
*/
@select() readonly authenticated$: Observable<boolean>;
canLoad(): Observable<boolean> | boolean {
// return true; // WORKS
return this.authenticated$; // ERROR: all routing stops from and to the current page
}
canActivate(): Observable<boolean> | boolean {
// return true; // WORKS
return this.authenticated$; // WORKS
}
}
lazy-routing.module:
//...
const routes: Routes = [
{
path: '',
component: LazyComponent
}
];
//...
Expected behavior
Like the canActivate
guard the canLoad
guard should subscribe to the Observable.
Minimal reproduction of the problem with instructions
- set value of the
authenticated$
Observable to true - lazy load a module and protect it via
canLoad
guard - navigate to the lazy and protected module via
<a routerLink="/lazy">Lazy</a>
=> all navigation stops.
Environment
Angular version: 4.3.6
Browser:
- [x] Chrome (desktop) version 60.0.3112.113
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [x] Firefox version 55.0.3
- [x] Safari (desktop) version 10.1.2 (12603.3.8)
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
For Tooling issues:
- Node version: v8.4.0
- Platform: MacOS 10.12.6
Others:
* @angular-redux/router@6.3.1
* @angular-redux/store@6.5.7
* redux@3.7.2
* rxjs@5.4.3
* typescript@2.3.4
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 7
- Comments: 29 (11 by maintainers)
Commits related to this issue
- fix(router): add first operator to canLoad guards canLoad didn't use the first operator like other guards. close #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- update docs fix formatting Also add first operator to resolve. fix(router): add consistency between guards towards mutlvalued and infinite observables resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards towards mutlvalued and infinite observables convert all guards observable into single value observable using first operator. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards towards mutlvalued and infinite observables convert all guards observable into single value observable using first operator. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards towards mutlvalued and infinite observables convert all guards observable into single value observable using first operator. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards towards mutlvalued and infinite observables convert all guards observable into single value observable using first operator. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards convert all guards observable into single value observable using first operator. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards convert all guards observable into single value observable using first operator. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards convert all guards observable into single value observable using first operator. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards convert all guards observable into single emit observable. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards convert all guards observable into single emit observable. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
- fix(router): add consistency between guards convert all guards observable into single emit observable. resolve #18991 — committed to ghetolay/angular by ghetolay 7 years ago
Hello, we reviewed this issue and determined that it doesn’t fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on StackOverflow using tag
angular
.If you are wondering why we don’t resolve support issues via the issue tracker, please check out this explanation.
Angular does not complete your Observable for you, you must pass one that does. For example:
Observable used for guard must be completed.
@trotyl When I saw your first comment stating that guard’s observables must complete, I searched the docs to find something and I couldn’t find anything. Now that you link it, I think a sentence bury in the middle of the guide refactoring some demo code to implements a resolver isn’t what I would call well-documented but rather mentioned somewhere. And I understand why I couldn’t find that statement.
So if guard’s observables must complete I think we could do better on docs to stipulate it. But if that is the case I really don’t get why
canActivate
,canDeactivate
andcanActiveChild
all usefirst
operator on the observables. The purpose offirst
really seems to be to handle multi-value and possibly infinite Observables, I really can’t find any other reason.You’re right about the breaking changes, this will not pass the guard at the moment and it will with my PR. But the reason you mentioned is wrong :
This is wrong, for
canLoad
guard, the current behavior is to check all values so bothObservalbe.of(true, false)
andObservable.of(false, true)
will fail to pass the guard. But again this is inconsistent because while your example will fail acanLoad
guard it will pass acanActivate
guard.I forgot to check
resolve
so I thought onlycanLoad
acted differently. So there is a real inconsistency about guards behavior :canActivate
,canDeactive
andcanActivateChild
will only check the first value emitted and won’t wait for observables to complete.canLoad
will check all the values returned and wait until all observables complete.resolve
will only keep the last value emitted by each observable and wait until all observable complete.From that I think guards are mostly expecting single value observables that complete right after their single emit. That’s why I think Angular should shape all guard’s observable into single observable that complete right after (using
first
) and not users.I do also consider this a bug. canActivate is able to get the value out of the Observable, but canLoad isn’t. I haven’t seen anything about that in the documentation and it would be expected that the behaviour of both would be the same.
It can be reproduced in this testproject: https://gitlab.com/scaks/myTestApp
Thanks for looking into it! Will create a question on SO.
@Toxicable: you don’t think the different behavior of the two guards could be considered a bug? Maybe it just needs more documentation 😃
Alright, that was something I overlooked then.
My point still stands though - Code should work for all guards the same. canLoad should act the same in terms of observables like canActivate
Let’s refocus that issue.
I’ve rework the plunker of @Toxicable to suits original post. http://plnkr.co/edit/Vr05o12PRrVmpGXwjkwq?p=preview
So while both guard has the same implementation only one works. This may be a docs issue, but I agree it’s disturbing that all guard doesn’t have the same requirement. Also I couldn’t find anything on the docs saying observable returns by
canLoad
must complete.@bunnyvishal6 Your have a problem with rxjs (how to import operators) and not with any of Angular or guards. I’ll be pleased to help you on gitter or stackoverflow if you want, but here isn’t the place for that.
There has been discussion before at https://github.com/angular/angular/issues/9613, even it was not be regarded as an Angular bug, a PR https://github.com/angular/angular/pull/10412 still be merged for that.
I agree that they should be consistent anyway.
I don’t think the type of module matters, it’s just about guard types.
I think I found the problem and that this may be an actual bug on
canLoad
. I’ll open a PR soon about it.@Z3roCoder you’ve made the same mistake as @markusfalk . the Observable returned from
new BehaviorSubject(true)
does not complete after it’s first emission, therefore the canLoad does not complete. You need to return an Observable that does complete, using an operator such astake
can help you with that. For examplereturn this.obj.take(1);