3

I'm trying to understand how to load configuration files in Angular 6, I currently have a ConfigureService with the following code for loading the file:

configUrl = '../assets/config.json';
config: Config;

load() {
  console.log('loading configuration');
  this.getConfig().subscribe(
    (data: Config) => {
      console.log('Config request success');
      (this.config = data);
      console.log(this.config)
    }
  );
}

private getConfig() {
  return this.http.get<Config>(this.configUrl);
}

Where load is called in the constructor of ConfigureService

I use this service to get my Url strings for my api consumer services, and it does run successfully (the console.logs show in the dev window), just not in time for those services to use their respctive url strings in their OnInit methods, and the other services throw errors as they are trying to pull the strings from an undefined config object.

I have tried to read them in by specifying them in the app.module and loading them before startup as seen here:

https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9

Yet when I try that method, it tells me that the files could not be found on the server, and the error in the console returns 404, even though the path specified is correct.

How can I ensure that the config service runs first so that other services who are dependent on it do not try to retrieve data before it has finished its initialization?

Mason Turvey
  • 165
  • 1
  • 13

3 Answers3

3

It is a very good question,We too have different servers ( Dev, QC ,Stage ,Prod), so creating a separate build for every environment is very time consuming process , I have never tried this approach of creating separate environment file for every environment, We solved this problem by storing the Api Urls and constants in a json file.

so first of all create a json file and place it inside assets folder.

Config.json

{
    "url":{
    "apiUrl": "http://localhost:58357/"
    }
}

Create a model class which should have the properties with same name as is Config.json file

Config.ts

 export class Config {
    url: {
        apiUrl: string;
    };
}

Create a service to import Config.json file

app.config.service.ts

import { Injectable } from '@angular/core';
import { Config } from './models/config';
import { HttpClient, HttpBackend, HttpResponse } from '@angular/common/http';
import { Observable } from '../node_modules/rxjs';
import { map } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
})
export class AppConfig {

static Settings: Config;
private http: HttpClient;
constructor(private httpBackEnd: HttpBackend) {
    this.http = new HttpClient(httpBackEnd);
}
load() {
    const jsonFile = 'assets/config.json';
    return new Promise<void>((resolve, reject) => {
    this.http.get(jsonFile).toPromise().then((response: Config) => {
       AppConfig.Settings = <Config>response;
       resolve();
    }).catch((response: any) => {
       reject(`Could not load file '${jsonFile}': ${JSON.stringify(response)}`);
    });
    });
 }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { AppComponent } from './app.component';
import { AppConfig } from '../app.config.service';
import { HttpClientModule } from '../../node_modules/@angular/common/http';


export function initConfig(appConfig: AppConfig) {
return () => appConfig.load();
}

@NgModule({
declarations: [
AppComponent
],
  imports: [
  BrowserModule,
  HttpClientModule,
  ],
providers: [
 AppConfig, { provide: APP_INITIALIZER, useFactory: initConfig, deps: [AppConfig], multi: true },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

import AppConfig in any component file where you want to use the keys stored in the json file.

app.component.ts

import { Component } from '@angular/core';
import { AppConfig } from '../app.config.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
 })
 export class AppComponent {
 title = 'ConstantsManagerDemo';
 constructor() {
 const apiUrl = AppConfig.Settings.url.apiUrl;
 alert(apiUrl);
  }
 }

go to tsconfig.json file and add

"allowSyntheticDefaultImports" :true,  
under compilerOptions
Mohan Singh
  • 1,142
  • 3
  • 15
  • 30
  • This only works when running on local machine but fails when deployed on test server. When the angular runs on client browser there is no way it has access to assets folder on the server unto which the angular package has been deployed. There is no way angular when running on client browser will be able to find the file for this line const jsonFile = 'assets/config.json'; – Sike12 May 24 '20 at 16:52
  • @Sike12 ,you set `"allowSyntheticDefaultImports" :true`, under compilerOptions in tsconfig.json file? – Mohan Singh May 25 '20 at 03:49
  • i did and it doesnt work. I have a feeling because i am deploying the app on azure things work differently. – Sike12 May 25 '20 at 08:01
  • may be, Because i have deployed a site on production server with same code and it works fine. – Mohan Singh Jun 09 '20 at 04:47
0

You probably want to use async/await or forward promises:


    config: Promise<Config>;

    load() {
      console.log('loading configuration');
      this.config = requestConfig();
    }

    private requestConfig(): Promise<Config> {
       return this.http.get<Config>(this.configUrl).toPromise();
    }

    // do something asynchronously based on the config
    public async doAsynchonously(consumer: (Config => void)){
      const config = await this.config;
      consumer(config);
    }

    // return another promise that someone can react on, or use asynchronously
    // i.e. push handling of asynchronous behavior to the user of the API
    public someSettingFromConfig(): Promise<string> {
       return this.config.then(config => config.somesetting);       
    }

See also: http://www.damirscorner.com/blog/posts/20170127-Angular2TutorialWithAsyncAndAwait.html
And: https://labs.encoded.io/2016/12/08/asyncawait-with-angular/

Another thing you might want to do, is issuing a synchronous request, and I'm giving a warning here: "Synchronous requests are usually bad for performance!".

    config : Config;

    load() {
      this.config = requestConfig();
    }

    private requestConfig(): Config {
      const request= new XMLHttpRequest();
      request.open('GET', '/assets/config.json', false);
      if (request.overrideMimeType) {
        request.overrideMimeType('application/json');
      }
      request.send();
      if (request.status === 200) {
        return <Config>JSON.parse(config.responseText);
      }
      // log some error
      return undefined;
    }
Brixomatic
  • 381
  • 4
  • 16
0

Follow these steps:

  • Import environment
  • Define local environment property
  • Read the configuration property
import {environment} from '../../environments/environment';
env = environment;
public myProperty = this.env.property path
robsiemb
  • 6,157
  • 7
  • 32
  • 46