Can't log in using observable and lazy loading

Viewed 61

I'm new to angular and i'm trying to implementate a login functionality.

The problem is that after my user hits login it should storage the token and then redirect to the Home page, but the canActivate returns false.

Obs: I'm using observable cuz i need to hide my navBar in the login page, and the best way that i found is by using *ngIf and getting the value of isLoggedIn observable.

AuthService.ts

import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Router } from "@angular/router";
import { ToastrService } from "ngx-toastr";
import { BehaviorSubject, Observable, Subject } from "rxjs";
import { BaseService } from "./base.service";

@Injectable() 
export class AuthService extends BaseService{

private loggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

get isLoggedIn(){
    return this.loggedIn.asObservable();
}

constructor(http: HttpClient, private router : Router, private toastr : ToastrService) {
    super(http);
}

login(body: any){
    return this.post('User/login', body).subscribe(
        (res: any) => {
          localStorage.setItem('token', res.token);
          this.loggedIn.next(true);
          this.router.navigate(['/home']);                       
        },
        err => {
            if (err.status)
              this.toastr.error('FAIL!.');}
    )
}

logout(){
    console.log(this.isLoggedIn)
    this.loggedIn.next(false);
    localStorage.removeItem('token');
    this.router.navigate(['/login']);
}
}

home.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from '../services/auth.service';


@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

isLoggedIn$!: Observable<boolean> | Promise<boolean> | boolean;

constructor(private router : Router, private authService : AuthService) { }

ngOnInit(): void {
 this.isLoggedIn$ = this.authService.isLoggedIn;
}

onLogout(){
 this.authService.logout();
}
}

auth.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, CanActivateChild } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {

  constructor(private router: Router, private toastr : ToastrService, private authService : AuthService) {
  }

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean  {
    return this.authService.isLoggedIn.pipe(
      take(1),
      map((isLoggedIn: boolean) => {
        if (!isLoggedIn) {
          this.router.navigate(['/login']);
          this.toastr.error("User not loged in!");
          return false;
        }
        return true;
      })
    );
  }
}

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './auth/auth.guard'; 

const routes: Routes = [
  {
    path: '',
    redirectTo: "/login",
    pathMatch: 'full'
  },
  {
    path: 'home', canActivate: [AuthGuard],
    loadChildren: () => import('./home/home.module').then(m => m.HomeModule)
  },
  {
    path: 'login',
    loadChildren: () => import('./login/login.module').then(m => m.LoginModule)
  },
  {
    path: 'registration',
    loadChildren: () => import('./registration/registration.module').then(m => m.RegistrationModule)
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
2 Answers

you should remove AuthService from LoginModule, because it creates another copy of this service in that module injector, and, because of that the other instance doesn't get the login state

Try to declare the AuthService as a singleton service.

@Injectable({
  providedIn: 'root',
})

Also remember that the BehaviorSubject emits the last value to new subscribers immediately

Related