3

Basically everything works correctly until for some reason when the url is typed the variable this.estado within the method canActivate happens to be undefined.

I think that this because the constructor does not get the observable at the correct time.

import { Component, Injectable, Inject, OnInit } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';


@Injectable()
export class AuthService implements CanActivate {
  myAppUrl: string;
  estado: any;

  constructor(private router: Router, private http: HttpClient, @Inject('BASE_URL') private baseUrl: string) {
    this.myAppUrl = baseUrl;
    this.estadoSetUp(); /* If I put hear this.estado = true everything works fine */
  }

  public getJSON(): Observable<any> {
    return this.http.get(this.myAppUrl + 'api/IsAuthenticated');
  }

  public estadoSetUp() {
    this.getJSON().subscribe(data => {
      this.estado = data.AmILoggin;
    });
  }

  canActivate(): Observable<boolean> {
    if (this.estado != true) {
      this.router.navigate(['/']);
    }

    return this.estado;
  }
}

SOLVED thanks to @sirdieter

I leave here the solution for anyone having trouble in the future:

import { Component, Injectable, Inject, OnInit } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot       } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject'
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/do';

@Injectable()
export class AuthService implements CanActivate {
  myAppUrl: string;
  private isAuthorized = new ReplaySubject<boolean>(1);

  constructor(private router: Router, private http: HttpClient,     @Inject('BASE_URL') private baseUrl: string) {
    this.myAppUrl = baseUrl;
    this.estadoSetUp();
  }

  public getJSON(): Observable<any> {
    return this.http.get(this.myAppUrl + 'api/IsAuthenticated');
  }

  public estadoSetUp() {
    this.getJSON().subscribe(data => {
      this.isAuthorized.next(data.AmILoggin);
    });
  }

  canActivate(): Observable<boolean> {
    return this.isAuthorized.asObservable()
      .do(auth => {
        if (auth != true) {
          this.router.navigate(['/']);
        }
      });
  }
}
Kelevra
  • 35
  • 5

2 Answers2

2

Be aware that your http call is async,

Therefor, when you open an URL directly, this guard is constructed (it starts the http call), AND very soon after that (milliseconds) the "canActivate" is called. As a result, if your http call wasn´t fast enough, your variable isn´t defined, because there are no results from the http call.

canActivate returns an Observable of a boolean. So one solution would be to change your variable estado to an observable

JanRecker
  • 1,787
  • 13
  • 17
  • Yeah, I have try that, but then how can I compare this.estado to true? I am new to angular and I am so lost... – Kelevra Jul 17 '18 at 11:05
1

You correctly found out the problem. You can return the http call observable and map it to the AmILoggin property. (RxJS 6 Syntax, you seem to have 5)

canActivate(): Observable<boolean> {
    return this.getJSON().pipe(pluck('AmILoggin');
}

But in this case you would make the http call everytime canActivate() is called; To only have one http call you could use a Subject:

private isAuthorized = new ReplaySubject<boolean>(1);

public estadoSetup() {
    this.getJSON().subscribe(data => this.isAuthorized.next(data.AmILoggin));
}
// RxJS 6
canActivate() {
    return this.isAuthorized.asObservable().pipe(
      tap(auth => {
        if (auth) {
            this.router.navigate(['/']);
        }
      }
    );
}
// RxJS 5
canActivate(): Observable<boolean>
    return this.isAuthorized.asObservable()
    .do(auth => {
        if (auth) {
            this.router.navigate(['/']);
        }
    });
SirDieter
  • 309
  • 2
  • 9
  • your code seems to work for me, but is there any way in which I can compare this.isAuthorized.asObservable(); to true or false? – Kelevra Jul 17 '18 at 11:29
  • You can use the tap (RxJS 6)/ do (RxJS 5) operator for it. Check my edit for it. – SirDieter Jul 17 '18 at 12:08
  • I can't manage your second code to work, it gets me an error on the canActivate. I have made a temporary solution. Using your first code. It's kind of crazy because it makes a ton of petitions for such a simple thing, but at least works for now :( `this.estado = this.getJSON().pipe(pluck('AmILoggin')); this.estado.subscribe(val => { if (val == false) { this.router.navigate(['/']) } }); return this.estado;` – Kelevra Jul 17 '18 at 12:57
  • what version of angular do you use? – SirDieter Jul 17 '18 at 13:04
  • We are using angular 5, because it's the last template that comes with asp.net core 2 without doing any tricks or at least that's what they told me. – Kelevra Jul 17 '18 at 17:12
  • I have edited my answer to include a RxJS 5 answer. Don't forget to import the do operator. – SirDieter Jul 17 '18 at 18:26
  • I can't express how grateful I am... Thank you so much for the help. @sirdieter – Kelevra Jul 18 '18 at 07:21
  • @Pablo to your edit: you only needed because I forgot to add the type for ReplaySubject. So I fixed it: ReplaySubject – SirDieter Jul 18 '18 at 08:24