Behavior Subject Becomes Null When Changing routes

1k Views Asked by At

I am trying to set up a check to make sure a user is authenticated in my header component to give access to routes that are only available for logged in users. That part works, however when I try to use one of those routes after I am logged in, it will change my user behavior subject back to null rendering my isAuthenticated false as well. I am not sure what is going on here, as I am not receiving errors either.

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject, Subject } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { User } from '../models/user.model';

interface LoginData {
  first_name?: string;
  last_name?: string;
  email?: string;
  password?: string;
}

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  apiUrl = 'http://localhost:3000/api/v1/users/';
  tokenExp: Date = new Date();
  user = new BehaviorSubject<User | null>(null);

  constructor(private http: HttpClient, private router: Router) {}

  login(form: LoginData) {
    return this.http.post(this.apiUrl + 'login', form).pipe(
      map((res: any) => {
        const user = res;
        console.log(user);
        if (user.success) {
          localStorage.setItem('Bearer', user.payload.token.value);
          const newUser = new User(
            user.payload.user.email,
            user.payload.user.first_name,
            user.payload.user.last_name,
            user.payload.user.blogs,
            user.payload.user.id,
            user.payload.token
          );
          this.user.next(newUser);
        }
      })
    );
  }
.
.
.
}

Above is my authentication service where I set the user to my behavior subject.

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/auth/services/auth.service';

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.scss'],
})
export class NavigationComponent implements OnInit, OnDestroy {
  isAuthenticated: boolean = false;
  private userSub: Subscription;

  constructor(private authService: AuthService) {
    this.userSub = this.authService.user.subscribe((user) => {
      this.isAuthenticated = !!user;
    });
  }

  ngOnInit(): void {}

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

  ngOnDestroy(): void {
    this.userSub.unsubscribe();
  }
}

This is my navigation component where I subscribe to that behavior subject and set isAuthenticated to false if my BS is null and true if there is value.

The problem lies when I try to click on any route after I log in. Once I click a route it will set is authenticated to false and my user to null. For some reason it is changing the value of my behavior subject and I don't know why.

Here is the navigation template:

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">Sicktastic Blogs</a>
    <button
      class="navbar-toggler"
      type="button"
      data-bs-toggle="collapse"
      data-bs-target="#navbarNav"
      aria-controls="navbarNav"
      aria-expanded="false"
      aria-label="Toggle navigation"
    >
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li
          class="nav-item"
          routerLink="/home"
          routerLinkActive="active"
          *ngIf="isAuthenticated"
        >
          <a class="nav-link" aria-current="page" href="#">HOME</a>
        </li>
        <li
          class="nav-item"
          routerLink="/profile"
          routerLinkActive="active"
          *ngIf="isAuthenticated"
        >
          <a class="nav-link" aria-current="page" href="#">PROFILE</a>
        </li>
        <li
          class="nav-item"
          routerLink="/register"
          routerLinkActive="active"
          *ngIf="!isAuthenticated"
        >
          <a class="nav-link">REGISTER</a>
        </li>
        <li
          class="nav-item"
          routerLink="/login"
          routerLinkActive="active"
          *ngIf="!isAuthenticated"
        >
          <a class="nav-link">LOGIN</a>
        </li>
        <li class="nav-item" *ngIf="isAuthenticated">
          <a class="nav-link" (click)="onLogout()">LOGOUT</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
1

There are 1 best solutions below

1
On BEST ANSWER

The issue is that you have href="#" in your links causing your page to reload when you go to a link, which in turn causes your components' states to reset.

Your routerLink directives need to go on your anchor elements, not the list items. You can keep the RouterLinkActive directive on the parent element if you need it there as it will pick up if any descendant router links are active.

It looks like you're using Bootstrap? If the link not taking the entire width of its parent is an issue, then use the stretched-link class.

<li class="nav-item" *ngIf="!isAuthenticated" routerLinkActive="active">
    <a class="nav-link stretched-link" [routerLink]="['/login']">LOGIN</a>
</li>