Search code examples
angularkeycloak

How to import keycloak module using standalone components and without NgModule (v17)


I've just created a new angular application (v17). AppComponent is a standalone component and main.ts looks like

bootstrapApplication(AppComponent, appConfig)
  .catch((err) => console.error(err));

When the app boots I want (if not done yet) keycloak to kick to authenticate and redirect to the keycloak login page.

So I modified appConfig:

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    {
      provide: APP_INITIALIZER,
      useFactory: initializeKeycloak,
      multi: true,
      deps: [KeycloakService],
    }
  ]
};

The problem here is that KeycloakService is never provided. If you google you see examples which use NgModule in which KeycloakAngularModule is added to the imports array, which I don't have (everything is standalone components.

So my question is, should I create an app.module.ts file an do

@NgModule({
    imports: [ KeycloakAngularModule, ...],
    ...
})
export class AppModule { }

or is there an other way to do this with angular 17?


Solution

  • Simply add the KeycloakService as provider.

    export const appConfig: ApplicationConfig = {
      providers: [
        provideRouter(routes),
        {
          provide: APP_INITIALIZER,
          useFactory: initializeKeycloak,
          multi: true,
          deps: [KeycloakService]
        },
        KeycloakService // <-- add your missing provider here
      ]
    };
    

    UPDATE
    This answer specifically fixes the following error:

    NullInjectorError: No provider for _KeycloakService!
    

    Here is a more complete version of the app.config.ts file with a factory function from Keycloak Angular#Setup:

    import { APP_INITIALIZER, ApplicationConfig } from '@angular/core';
    import { provideRouter } from '@angular/router';
    import { KeycloakService } from 'keycloak-angular';
    
    import { routes } from './app.routes';
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideRouter(routes),
        {
          provide: APP_INITIALIZER,
          useFactory: initKeycloak,
          multi: true,
          deps: [KeycloakService]
        },
        KeycloakService
      ]
    };
    
    function initKeycloak(keycloak: KeycloakService) {
      return () => keycloak.init({
        config: {
          url: 'http://localhost:8080',
          realm: 'your-realm',
          clientId: 'your-client-id'
        },
        initOptions: {
          onLoad: 'check-sso',
          silentCheckSsoRedirectUri: window.location.origin
            + '/assets/silent-check-sso.html'
        },
      });
    }
    

    Your standalone components should also import KeycloakAngularModule if needed:

    import { Component } from '@angular/core';
    import { RouterOutlet } from '@angular/router';
    import { KeycloakAngularModule } from 'keycloak-angular';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [
        RouterOutlet,
        KeycloakAngularModule,
      ],
      templateUrl: './app.component.html',
      styleUrl: './app.component.scss'
    })
    export class AppComponent {
      title = 'keycloak';
    }