4

Before every request I want to be sure that there is a user profile available. I use a canActivateChild guard to do this.

According to the documentation of angular2 it is possible to return an observable: https://angular.io/api/router/CanActivateChild

app.routes.ts

export const routes: Routes = [
  {
    path: '',
    canActivateChild: [ProfileGuard],
    children: [
      {
        path: 'home',
        component: HomeComponent,
        canActivate: [AuthGuard]
      },
      {
        path: 'login',
        component: LoginComponent,
        canActivate: [GuestGuard]
      },
 }
];

The canActivatedChild is executed before the child route canActivate

profile.guard.ts:

export class ProfileGuard implements CanActivateChild {

  constructor(private profileService: ProfileService, private snackbar: MdSnackBar) { }

  canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean>  {

    if (this.profileService.user == null && localStorage.getItem('__token') != null) {
      return this.profileService.loadProfile().map((user) => {
        console.log('profile loaded');
        this.profileService.user = <UserModel>user.data;
        return true;
      });
    }
    return true;
  }

}

Load profile function:

  public loadProfile(): Observable<any> {
    console.log('load');
    return this.http.get('/profile').map(res => res.json());
  }

When I navigate through the menu (<a> with routeLink) the console.log('profile loaded') works. But if I reload the page with f5 or through the browser, it never gets there..

The console.log('load') is always executed.

EDIT:

If I in canActivateChild return:

return Observable.of(true).map(() => {
  console.log('Test');
  return true;
});

It works fine... I get the console.log('test')

Rick Lenes
  • 97
  • 4
  • In the F12 browser console on the networking tab, do you see the request to /profile and is it successful? – Hendrik Brummermann Jul 22 '17 at 14:54
  • This is an interesting problem. Do things work as expected if you just return `Observable.of(true)` instead of calling `this.profileService.loadProfile()` ? – BeetleJuice Jul 22 '17 at 14:55
  • Can you try a plnkr if possible , that way you get help very fast. You can use dummy data for HTTP calls – Vamshi Jul 22 '17 at 15:06
  • @HendrikBrummermann Yes the request is made and also successfull with the right data – Rick Lenes Jul 22 '17 at 17:12
  • If you `console.log` from within `canActivateChild()` (not within the Observable chain itself), does it work? – BeetleJuice Jul 22 '17 at 17:20
  • @BeetleJuice Yes this works.. So he is executing the code in `canActivateChild()` and `Observable.of(true)` does work as expected, I looked wrong the first time – Rick Lenes Jul 22 '17 at 17:32
  • Are you sure about your if statement? I think you need to check it, write console.log("test") before the if. – Hasan Beheshti Jul 23 '17 at 17:38
  • maybe it because of this.profileService.user == null, when you reload page with F5, this variable will be null, and if your localStorage.getItem("__token") was null, this problem will be happening – Hasan Beheshti Jul 23 '17 at 17:40
  • @HasanBeheshti I have already checked this, this is both not the problem. And if i watch in f12 network tab, then i see that the request is made and successfull – Rick Lenes Jul 24 '17 at 18:43

2 Answers2

1

I think it's resolving before you get a response, try adding in a replay subject that resolves to true or false once your request returns

import {Observable, ReplaySubject} from 'rxjs/Rx';

public loadProfile(): Observable<any> {
  console.log('loading profile');
  return this.http.get('/profile').map(res => res.json());
}

public isUserLoggedInObs(): Observable<boolean> {
  const userLoggedIn: ReplaySubject<boolean> = new ReplaySubject<boolean>;
  this.loadProfile.first().subscribe(
     user => userLoggedIn.next(user.loggedIn === true)
  )
  return userLoggedIn.asObservable();
}


canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean>  {
   return this.profileService.isUserLoggedInObs()
}
Daniel Swiegers
  • 4,631
  • 1
  • 8
  • 13
0

I was running on a RC of @angular/router. After i updated this it worked like expected.

Rick Lenes
  • 97
  • 4