Search code examples
angularsignalsangular-standalone-components

Angular authentication signal works when logging in but not when logging out


I have a component that shows the logged in status simply like this:

{{loggedIn()}}

the .ts code is as follows:

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, WritableSignal } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { RouterOutlet } from '@angular/router';
import { CompanyLoginComponent } from './company-login/company-login.component';
import { AuthService } from './auth.service';
import { AsyncPipe } from '@angular/common';
import { MatSidenavModule } from '@angular/material/sidenav';


@Component({
    selector: 'app-company-facing',
    standalone: true,
    imports: [
        MatToolbarModule,
        MatButtonModule,
        MatIconModule,
        RouterOutlet,
        CompanyLoginComponent,
        AsyncPipe,
        MatSidenavModule,
    ],
    templateUrl: './company-facing.component.html',
    styleUrl: './company-facing.component.scss',
    changeDetection: ChangeDetectionStrategy.OnPush,
    providers: [AuthService,]

})
export class CompanyFacingComponent {

    constructor(private auth: AuthService,
        private cdr: ChangeDetectorRef,
    ) {
    }

    get loggedIn() {
        return this.auth.loggedIn;
    }

    async logout() {
        await this.auth.logout();
    }
    forceChangeDetection() {
        this.cdr.detectChanges(); // Where cdr is ChangeDetectorRef injected in your constructor
    }
}

here's my auth service:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { firstValueFrom } from 'rxjs';
import { signal, WritableSignal } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {


  loggedIn: WritableSignal<boolean> = signal(false);

  constructor(
    private http: HttpClient,
  ) {
    this.loggedIn.set(!!localStorage.getItem('accessToken'));
  }

  async login(email: string, password: string) {
    let res = await firstValueFrom(
      this.http.post<{ access_token: string }>('https://api.self-serve.just-solve.com/api/login',
        {
          email,
          password
        })
    )
    console.log(res);
    if (res.access_token) {
      this.loggedIn.set(true);
      console.log("logged in updated to true!");
      //FIXME: wHY NOT WORKING CHANGE DETECTION on company-facing.component.ts??? when it log out, it works, when log in, it stays stuck and you have to refresh page ç_ç
    }

    console.log(this.loggedIn())
    localStorage.setItem('accessToken', res.access_token);
    return res;
  }


  async logout() {
    try {
      let res = await firstValueFrom(
        this.http.post('https://api.self-serve.just-solve.com/api/logout', {})
      );
      this.loggedIn.set(false);
    }
    catch (e) {
      console.error(e);
    }

    localStorage.removeItem('accessToken');
  }
}

The problem is: When i log out, the logged in display correctly switches to false. When i log in, it stays to true, and i have to refresh the page to make it update. How come? How can i make it update also when i log in and not only when i log out?


Solution

  • Solved!

    I had written providers: [AuthService] within the @Component decorator, which led to AuthService being instantiated per component, rather than as a singleton across the entire application.

    As a result, the logout functionality worked as expected because it was invoked from the same component instance that initiated the AuthService. However, the login action, triggered from a different component, was operating on a separate instance of AuthService.

    This created a discrepancy where the login updated a different instance of the loggedIn value, which was not being observed by the main component.

    To solve this, I removed providers: [AuthService] from all the @Component decorators and ensured AuthService is provided at the application level, by using providedIn: 'root' in the @Injectable decorator of the AuthService. This change ensures that a single instance of AuthService is used throughout the application, maintaining consistent state across components.