0

I'm using angular 5 and httpclient. I'm not a big fan of the built in environments to set up my environment api url's that I need to consume. I have switched to using an nginx.conf where I make an api call that returns a json with my api urls that is set from an environment variable.

I have a service where I'm making an http.get call to grab the json config file.

@Injectable()
export class ConfigurationService {
private baseUrl = 'https://someUrl.com';

constructor( private http: HttpClient ) {

}

getConfig(): Observable<any> { 
  return this.http
  .get(this.baseUrl + '/config')
  .map(res=>res);   
};
}

This service is called from my app.component.ts when the application first starts.

I have a constants.ts file that I want to use to reference my api urls

export class Constants {    

public api1 = //NEED API1 URL FROM SERVICE HERE;
public api2= //NEED API2 URL FROM SERVICE HERE;

What is the best way to bring in the data from the service? My constants file is not a component just a class.

3 Answers 3

1

For something like this you should use an app initializer service. An app initializer runs before everything else in your application and your app won't load until it's done. It's structured like this:

import { Injectable, APP_INITIALIZER } from '@angular/core';

export interface AppConfig {
    api1: string;
    api2: string;
}

@Injectable()
export class ConfigService {
  protected config: AppConfig;

  constructor(private _http: HttpClient) {
  }

  getConfigData(): Observable<any> {
    return this._http.get<AppConfig>('... your config url...')
      .catch(error => {
        alert('Unable to read configuration file:' + JSON.stringify(error));
        return Observable.throw(error);
      });
  }

  public load() {
     return new Promise((resolve, reject) => {
       this.getConfigData()
         .subscribe(
           config => {
             this.config= config ;
             resolve(true);
           },
           err => resolve(err)
         );
     });
  }
}

export function ConfigServiceInitFactory(configService: ConfigService) {
  return () => configService.load();
}

export const ConfigServiceInitProvider = {
  provide: APP_INITIALIZER,
  useFactory: ConfigServiceInitFactory,
  deps: [ConfigService],
  multi: true
}

then you add the ConfigServiceInitProvider to your AppModule providers along with the ConfigService (put ConfigService before ConfigServiceInitProvider) and just inject the ConfigService where needed and access the config values like:

constructor(private _config: ConfigService) {
    this.apiBase = this._config.config.api1;
}

I don't like the repetitiveness of the _config.config so i also will usually define getters on my config service like:

get api1() { return this.config.api1 }

and then you can just call:

this.apiBase = this._config.api1;

HOWEVER, if you're asking if it's possible to set values in a constants.ts file so that it can be used like:

import {Constants} from 'app/constants';

constructor() {
   this.apiBase = Constants.api1;
}

that cannot be done with something loaded from your server at runtime, because all your typescript is compiled to javascript when you run your build commands. So you logically can't create your build with something loaded from the server without it being provided as a service value. You will always have to inject a service.

The only way around this is to insert a different constants file PRIOR to running your build, then you never need to call your config from your server. But this has drawbacks of it's own such as it requiring a full rebuild and redeploy to update config values which is kind of against the point of a config in the first place.

Sign up to request clarification or add additional context in comments.

2 Comments

This worked perfectly. Per your comment I did not set the values in the constants.ts file. Instead I injected the configuration service into my other service that makes the api calls. I do have one question though why did you make config protected? protected config: AppConfig; I had to make it public to access api1 and api2
I copied the service I use in basically all of my projects. I make it protected because I work with other developers on things and I don't want anyone to be able to change config values that affect the entire application. I define public getters like I outlined in my post to get the data out. Should at least define a private _config and public get config() that delivers a copy of it, not the original.
0

You should be having the constant class as an @Injectable and have methods to set the value as below

@Injectable()
export class Constants {    

   api1 :string; // NEED API1 URL FROM SERVICE HERE;

   api2: string; // NEED API2 URL FROM SERVICE HERE


   setValueApi1(val){
       this.api1 = val;
   }
}

You can inject this to the constructor and call the method to set the value by passing the response as below

@Injectable()
export class ConfigurationService {
private baseUrl = 'https://someUrl.com';

constructor( private http: HttpClient,private constants:Constants ) {  }

    getConfig(): Observable<any> { 
      return this.http
      .get(this.baseUrl + '/config')
      .map(res=>res)
    }
}

In your component you can subscribe to the response value and set the api value by calling the method as below

this.getConfig().subscribe(response => {
    this.constants.setValueApi1(response))
})

1 Comment

thank you for this. I did implement this and it works just fine, I just happened to go with the above answer with the APP_INITIALIZER
0

A simple way to do it is like this:

class Constants {
    public api1: string;
    public api2: string;

    public setApi1(api: string) {}
    public setApi2(api: string) {}
}

export const CONSTANTS: Constants = {
    api1: "DEFAULT API 1",
    api2: "DEFAULT API 2",
    setApi1(api: string) : void {
        this.api1 = api;
    },
    setApi2(api: string) : void {
        this.api2 = api;
    }
}

Then just import your const wherever you need the persistence:

import {CONSTANTS} from "../path/to/constant.ts";

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.