Как загрузить конфигурацию при инициализации приложения

Вопрос или проблема

Когда я включаю provideEffects(ConfigEffects) в main/ts -> app.config.ts, я получаю следующую ошибку

TypeError: this.actions$ не определен
    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

Почему this.actions$ не внедряется? Я пропускаю какие-то конфигурации?
Буду благодарен за любую помощь.
Пожалуйста, дайте знать, если нужны дополнительные детали.

У меня есть конфигурация в public/config/config.json следующего вида

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

Мой 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 })))
        )
      )
    )
  });

}

В предыдущем коде loadConfig$ инициализируется до того, как actions$ получит значение, поэтому вы можете столкнуться с этой проблемой.


Один из способов решить это – установить "useDefineForClassFields": false в tsconfig.json, если вы используете ES2022.


Вы также можете попробовать inject перед определением loadConfig$, чтобы actions$ всегда было доступно.

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);
  }
}

Связанная проблема на GitHub 1

Связанная проблема на GitHub с обходными путями


Ответ или решение

Чтобы загрузить конфигурацию при инициализации приложения Angular и избежать ошибки, связанной с отсутствием зависимости this.actions$, вы можете использовать несколько подходов. Рассмотрим их.

Проблема

Ошибка TypeError: this.actions$ is undefined, как правило, возникает из-за того, что loadConfig$ инициализируется до того, как actions$ получает значение. Это может происходить, если вы используете ES2022 и настройки компиляции TypeScript по умолчанию.

Решение 1: Изменение tsconfig.json

Одним из способов решения этой проблемы является добавление "useDefineForClassFields": false в ваш tsconfig.json. Это изменение переключает поведение объявления свойств классов и может помочь избежать этой проблемы.

{
  "compilerOptions": {
    "useDefineForClassFields": false,
    // другие настройки...
  }
}

Решение 2: Использование inject

Вместо того чтобы инициализировать actions$ в конструкторе, вы можете воспользоваться функцией inject для его получения. Это гарантирует, что actions$ всегда будет доступен.

Вот переписанный код вашего config.effects.ts:

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$ = inject(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);
  }
}

Общее решение

В вашем app.config.ts вам стоит убедиться, что все необходимые провайдеры и зависимости подключены корректно:

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }), 
    provideRouter(routes),
    provideHttpClient(),
    provideStore({ config: configReducer }),
    provideEffects(ConfigEffects)  // Убедитесь, что этот эффект правильно регистрируется
  ]
};

Завершение

После внесения указанных изменений, ваша проблема с this.actions$ должна быть решена, и вы сможете корректно загружать конфигурацию при инициализации приложения. Если вам понадобится дополнительная помощь или возникнут другие вопросы, не стесняйтесь обращаться.

Оцените материал
Добавить комментарий

Капча загружается...