admin管理员组

文章数量:1123003

I'm learning with Angular 19. Now I tried to implement a service for authentication, which keeps an _isAuthenticated state as a class property. The service is marked as @injectable and provied in 'root' as the default setting. Then, I injected the service into other components, e.g. a route guard.

The problem is, when the route guard works, it gets a authentication service object from inject(). I would expect that Angular injects the servie as a singleton, however, in my case the obtained service is always a new object. When I set a breakpoint in the constructor of the service, it is always reached when the route guard takes effect. Then, of course only the initial value of the _isAuthenticated state is passed to the guard, which is "false".

This problem has confused me for some days. Any help would be great! Thanks!

My service for authentication:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, catchError, map, Observable, of } from 'rxjs';
import { User } from '../dto/user';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  
  private apiUrl = 'http://192.168.178.39:8080/auth';
  private _isAuthenticated = new BehaviorSubject<boolean>(false);

  constructor(private http: HttpClient) {
    localStorage.removeItem('JWT_Token');
  }

  login(username: string, password: string ): Observable<boolean> {
    return this.http.post<any>(this.apiUrl + '/login', {username, password})
      .pipe(
        map(response => {
          localStorage.setItem('JWT_Token', response.token);
          this._isAuthenticated.next(true);
          return true;
        }),
        catchError(error => {
          console.log(error);
          this.logout();
          return of(false);
        })
      );
  }

  logout(): void {
    localStorage.removeItem('JWT_Token');
    this._isAuthenticated.next(false);
  }

  get isAuthenticated() {
    return this._isAuthenticated.value;
  }

}

my route guard which injects the service:

import { CanActivateFn } from '@angular/router';
import { AuthService } from '../service/auth.service';
import { inject } from '@angular/core';

export const authGuard: CanActivateFn = (route, state) => {

  const authService = inject(AuthService);
  let isAuth = authService.isAuthenticated;

  return isAuth;
};

The app.routes.ts, where the route guard is put into use:

import { Routes } from '@angular/router';
import { HomeComponent } from './feature/home/homeponent';
import { LoginComponent } from './core/auth/login/loginponent';
import { BookListComponent } from './feature/bookstore/book-list/book-listponent';
import { authGuard } from './core/auth/guard/auth.guard';

export const routes: Routes = [
    {path: 'login', component : LoginComponent},
    {path: '', redirectTo: 'login', pathMatch : 'full'},
    {path: 'home', component : HomeComponent, canActivate : [authGuard]},
    {path: 'bookstore', component : BookListComponent, canActivate : [authGuard]},
];

finally, the app.config.ts

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { AuthInterceptor } from './core/auth/intercept/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }), 
    provideRouter(routes),
    provideHttpClient(withInterceptorsFromDi()),
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
  ]
};

I'm learning with Angular 19. Now I tried to implement a service for authentication, which keeps an _isAuthenticated state as a class property. The service is marked as @injectable and provied in 'root' as the default setting. Then, I injected the service into other components, e.g. a route guard.

The problem is, when the route guard works, it gets a authentication service object from inject(). I would expect that Angular injects the servie as a singleton, however, in my case the obtained service is always a new object. When I set a breakpoint in the constructor of the service, it is always reached when the route guard takes effect. Then, of course only the initial value of the _isAuthenticated state is passed to the guard, which is "false".

This problem has confused me for some days. Any help would be great! Thanks!

My service for authentication:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, catchError, map, Observable, of } from 'rxjs';
import { User } from '../dto/user';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  
  private apiUrl = 'http://192.168.178.39:8080/auth';
  private _isAuthenticated = new BehaviorSubject<boolean>(false);

  constructor(private http: HttpClient) {
    localStorage.removeItem('JWT_Token');
  }

  login(username: string, password: string ): Observable<boolean> {
    return this.http.post<any>(this.apiUrl + '/login', {username, password})
      .pipe(
        map(response => {
          localStorage.setItem('JWT_Token', response.token);
          this._isAuthenticated.next(true);
          return true;
        }),
        catchError(error => {
          console.log(error);
          this.logout();
          return of(false);
        })
      );
  }

  logout(): void {
    localStorage.removeItem('JWT_Token');
    this._isAuthenticated.next(false);
  }

  get isAuthenticated() {
    return this._isAuthenticated.value;
  }

}

my route guard which injects the service:

import { CanActivateFn } from '@angular/router';
import { AuthService } from '../service/auth.service';
import { inject } from '@angular/core';

export const authGuard: CanActivateFn = (route, state) => {

  const authService = inject(AuthService);
  let isAuth = authService.isAuthenticated;

  return isAuth;
};

The app.routes.ts, where the route guard is put into use:

import { Routes } from '@angular/router';
import { HomeComponent } from './feature/home/home.component';
import { LoginComponent } from './core/auth/login/login.component';
import { BookListComponent } from './feature/bookstore/book-list/book-list.component';
import { authGuard } from './core/auth/guard/auth.guard';

export const routes: Routes = [
    {path: 'login', component : LoginComponent},
    {path: '', redirectTo: 'login', pathMatch : 'full'},
    {path: 'home', component : HomeComponent, canActivate : [authGuard]},
    {path: 'bookstore', component : BookListComponent, canActivate : [authGuard]},
];

finally, the app.config.ts

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { AuthInterceptor } from './core/auth/intercept/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }), 
    provideRouter(routes),
    provideHttpClient(withInterceptorsFromDi()),
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
  ]
};

Share Improve this question asked 58 mins ago user29062502user29062502 1 New contributor user29062502 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Add a comment  | 

1 Answer 1

Reset to default 0

private _isAuthenticated = new BehaviorSubject(false); _isAuthenticated$ = this._isAuthenticated.asObservable();

then you either subscribe on the _isAuthenticated$ or await it with const isAuth = await firstValueFrom(auth.isAuthenticated);

As it is atm you only check the initial value with the get property. you never subscribe for changes.

本文标签: Angular 19why an Injectable service is created every time new when it is injectedStack Overflow