angularrxjsngrxauth0angular18

How to load configuration at app initialisation


When I include provideEffects(ConfigEffects) in main/ts -> app.config.ts, I got the following error

TypeError: this.actions$ is undefined
    ConfigEffects config.effects.ts:23
    createEffect Angular
    ConfigEffects config.effects.ts:20
    _ConfigEffects config.effects.ts:13
    ConfigEffects_Factory main.js:70
    Angular 18
    <anonymous> main.ts:5

Why this.actions$ is not injected ? Am I missing any configurations ? Any help would be appreciated ? Please let me know if any more details are required

I have a configuration in my public/config/config.json as follows

{
    "auth0": {
      "domain": "xxxx.us.auth0.com",
      "clientId": "xxxxx",
      "redirectUri": "http://localhost:4200",
      "logoutRedirectUri": "http://localhost:4200",
      "audience": "https://xxxxx/"
    }
  }
  

My main.ts -> app.config.ts


export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }), 
    provideRouter(routes),
    provideHttpClient(),
    provideStore({ config: configReducer }),
    provideEffects(ConfigEffects)
  ]
};

app.component.ts


@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {

  config$: Observable<any>;

  constructor(private store: Store) {
    this.config$ = this.store.select(selectConfig);
  }

  ngOnInit(): void {
    this.store.dispatch(loadConfig());
  }
}

config.effect.ts

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { ConfigService } from './config.service';
import * as ConfigActions from './config.actions';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { of } from 'rxjs';

@Injectable()
export class ConfigEffects {


  constructor(
    private actions$: Actions,
    private configService: ConfigService
  ) {
    console.log('Actions:', this.actions$);
    console.log('ConfigService:', this.configService);
  }
  
  loadConfig$ = createEffect(() =>
    {console.log('loadConfig...');
    
    return this.actions$.pipe(
      ofType(ConfigActions.loadConfig),
      mergeMap(() =>
        this.configService.loadConfig().pipe(
          map((config) => ConfigActions.loadConfigSuccess({ config })),
          catchError((error) => of(ConfigActions.loadConfigFailure({ error })))
        )
      )
    )
  }
  );

}

Solution

  • In the previous code, the loadConfig$ is initialized before the actions$ has a value, hence you might get this issue.


    One way to solve this might be to set "useDefineForClassFields": false in the tsconfig.json, if you are using ES2022.


    You can also try inject before the loadConfig$ is defined, so that actions$ is always available.

    import { Injectable, inject } from '@angular/core';
    import { Actions, createEffect, ofType } from '@ngrx/effects';
    import { ConfigService } from './config.service';
    import * as ConfigActions from './config.actions';
    import { catchError, map, mergeMap } from 'rxjs/operators';
    import { of } from 'rxjs';
    
    @Injectable()
    export class ConfigEffects {
      actions$: Actions = inject(Actions);
    
      loadConfig$ = createEffect(() => {
        console.log('loadConfig...');
    
        return this.actions$.pipe(
          ofType(ConfigActions.loadConfig),
          mergeMap(() =>
            this.configService.loadConfig().pipe(
              map((config) => ConfigActions.loadConfigSuccess({ config })),
              catchError((error) => of(ConfigActions.loadConfigFailure({ error })))
            )
          )
        );
      });
      constructor(private configService: ConfigService) {
        console.log('Actions:', this.actions$);
        console.log('ConfigService:', this.configService);
      }
    }
    

    Related Github issue 1

    Related Github issue with workarounds