@schaman/angular-config
v1.0.0
Published
This module provides configuration functionality. If you want to use the same built code for different environments, you can use this module to provide different configuration for each environment. This is the III. factor of the [Twelve-Factor App](https:
Downloads
64
Readme
@schaman/angular-config
This module provides configuration functionality. If you want to use the same built code for different environments, you can use this module to provide different configuration for each environment. This is the III. factor of the Twelve-Factor App.
Features
- [x] Provide configuration for different environments
- [x] Retry loading configuration
- [x] Guard for loading configuration
- [x] Signal support
- [x] RxJS support
- [ ] Pass configuration without load during SSR
Installation
npm install @schaman/angular-config
Usage
Provide the configuration in the assets/config.json
.
{
"backendUrl": "https://api.example.com"
}
Import the ConfigModule
in your AppModule
and provide the path to the configuration file.
import { ConfigModule } from '@schaman/angular-config';
@NgModule({
imports: [ConfigModule.forRoot()],
})
export class AppModule {}
Inject the ConfigService
in your component and use it.
import { ConfigService } from '@schaman/angular-config';
@Component({
selector: 'app-root',
template: ` <p>Backend URL: {{ config().backendUrl }}</p> `,
})
export class AppComponent {
public readonly config = inject(ConfigService).config;
}
Standalone component
If you want to use the ConfigService
in a standalone component, you have to import the ConfigModule
in the component module.
import { ConfigModule } from '@schaman/angular-config';
@Component({
selector: 'app-root',
template: ` <p>Backend URL: {{ config().backendUrl }}</p> `,
imports: [ConfigModule.forRoot()],
})
export class AppComponent {
public readonly config = inject(ConfigService).config;
}
Configuration
The ConfigModule
can be configured with the following options:
interface ConfigModuleOptions {
/**
* Number of retries when loading the config file.
* @default 3
*/
retry: number;
/**
* Path to the config file.
*/
configPath: string;
/**
* If true, the config will be loaded when the module is imported.
* @default true
*/
loadConfig: boolean;
/**
* If true, the config will be loaded when the app is initialized.
* @default false
*/
runAsAppInitializer: boolean;
}
Wait for config during app initialization
If you want to wait for the config to be loaded before the app is initialized, you can use the runAsAppInitializer
option.
import { ConfigModule } from '@schaman/angular-config';
@NgModule({
imports: [ConfigModule.forRoot({ runAsAppInitializer: true })],
})
export class AppModule {}
If you need the config in your app initializer, you can inject the ConfigService
and use the afterConfigLoad$
observable.
import { ConfigService } from '@schaman/angular-config';
@NgModule({
imports: [ConfigModule.forRoot({ runAsAppInitializer: true })],
providers: [
{
provide: APP_INITIALIZER,
useFactory: (configService: ConfigService) => () =>
configService.afterConfigLoad$.pipe(
tap((config) => {
/* use config here */
}),
),
deps: [ConfigService],
},
],
})
export class AppModule {}
Configure your generated backend base url
If you generate the backend using openapi-generator then provide the values like this:
import { ConfigModule } from '@schaman/angular-config';
import { ApiConfiguration } from 'your-data-package';
@NgModule({
imports: [ConfigModule.forRoot()],
providers: [
{
provide: ApiConfiguration,
useFactory: (configService: ConfigService): ApiConfiguration => {
const configuration = new ApiConfiguration();
configService.afterConfigLoad$.subscribe((config) => (configuration.basePath = config.backendUrl));
return configuration;
},
deps: [ConfigService],
},
],
})
export class AppModule {}
Use as guard
If not all routes should be available before the config is loaded, you can use the configLoadedGuard
.
import { configLoadedGuard } from '@schaman/angular-config';
const routes: Routes = [
{
path: '',
component: HomeComponent,
},
{
path: 'login',
canActivate: [configLoadedGuard],
component: LoginComponent,
},
];
Best Practices
If you have the interface defined in your code, then you can skip the Template parameter.
import { ConfigService } from '@schaman/angular-config';
import { computed } from '@angular/core';
interface Config {
backendUrl: string;
}
@Injectable()
export class MyConfigService {
private readonly configService = inject<ConfigService<Config>>(ConfigService);
public readonly config = this.configService.config;
public readonly config$ = this.configService.config$;
public readonly afterConfigLoad$ = this.configService.afterConfigLoad$;
// Computed signals can be added here, also.
public readonly backendUrl = computed(() => this.configService.config().backendUrl);
// Or RxJS operators
public readonly backendUrl$ = this.configService.config$.pipe(map((config) => config.backendUrl));
}
And provide it in your app module.
import { ConfigModule } from '@schaman/angular-config';
import { MyConfigService } from './my-config.service';
@NgModule({
imports: [ConfigModule.forRoot()],
providers: [MyConfigService],
})
export class AppModule {}