Search code examples
angulardependency-injectionangular18

Angular 18, adding dependencies to APP_INITIALIZER


I am trying to use angular v18's provider to provide a new APP_INITIALIZER with dependencies JwtService and Auth Service. here's the code snippet for the logics

export function initAuth(jwtService: JwtService, authService: AuthService) {
  return () => (jwtService.getToken() ? authService.getCurrentUser() : EMPTY);
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideHttpClient(withInterceptors([jwtInterceptor])),
    {
      provide: APP_INITIALIZER,
      useFactory: initAuth,
      deps: [JwtService, AuthService, HttpClient],
      multi: true,
    },
  ],
};

the service for JwtService:

@Injectable({ providedIn: 'root' })
export class JwtService {
  getToken(): string {
    return window.localStorage['jwtToken'];
  }

  setToken(token: string): void {
    window.localStorage['jwtToken'] = token;
  }

  destroyToken(): void {
    window.localStorage.removeItem('jwtToken');
  }
}

the service for AuthService:

@Injectable({ providedIn: 'root' })
export class AuthService {
  private apiUrl = environment.apiUrl + '/Auth';
  private http = inject(HttpClient);
  private router = inject(Router);

  private _loggedInUserSubject = new BehaviorSubject<User | null>(null);
  currentUser$ = this._loggedInUserSubject.asObservable();
  isAuthenticated$ = this.currentUser$.pipe(map((u) => u !== null));

  constructor(private jwtService: JwtService) {}

  register(credentials: RegisterCredentials): Observable<boolean> {
    return this.http
      .post<{ username: string }>(this.apiUrl + '/register', credentials)
      .pipe(map((user) => user.username === credentials.username));
  }

  login(credentials: LoginCredentials): Observable<User> {
    return this.http
      .post<User>(this.apiUrl + '/login', credentials)
      .pipe(tap((user) => this.setAuth(user)));
  }

  logout() {
    this.purgeAuth();
    this.router.navigate(['']);
  }

  getCurrentUser() {
    return this.http.get<User>(this.apiUrl).pipe(tap((u) => this.setAuth(u)));
  }

  private setAuth(user: User) {
    this._loggedInUserSubject.next(user);
    this.jwtService.setToken(user.token);
  }

  private purgeAuth() {
    this._loggedInUserSubject.next(null);
    this.jwtService.destroyToken();
  }
}

the problem is when I run the application it gave me this error: enter image description here

as far as I know, this error only occurs when I haven't add a provider for the services. So then I tried to add providers:

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideHttpClient(withInterceptors([jwtInterceptor])),
    JwtService,
    AuthService,
    {
      provide: APP_INITIALIZER,
      useFactory: initAuth,
      deps: [JwtService, AuthService, HttpClient],
      multi: true,
    },
  ],
};

And then I got a new error: enter image description here

How do I resolve this issue when I try to add dependencies to APP_INITIALIZER in angular 18? Or is there an issue that I am missing at?

EDIT:

After I checked the code, it appears that the AuthService is the reason for the 'Invalid provider' Error.


Solution

  • After trying out the deps and providers by adding some things, and removing it; I figure out that I need to add the dependencies by using inject(). (not deps)

    const initAuth = () => {
      const jwtService = inject(JwtService);
      const authService = inject(AuthService);
      return () => (jwtService.getToken() ? authService.getCurrentUser() : EMPTY);
    };
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideZoneChangeDetection({ eventCoalescing: true }),
        provideRouter(routes),
        provideHttpClient(withInterceptors([jwtInterceptor])),
        {
          provide: APP_INITIALIZER,
          useFactory: initAuth,
          multi: true,
        },
      ],
    };