Load Config JSON File In Angular 2
Asked Answered
M

5

16

I want to load Constant File in Angular 2(which is a Normal TypeScript File) having WebAPI EndPoints. In Angular1.x. we used to have constants for the same. How in Angular 2 I can Implement the Same?

I have created the .ts file.My main concern lies in how to load the file beforehand every Other class File loads.

.ts file :

export class testAPI {
     getAPI = "myUrl";
}

In service file I am using the same by doing Normal Import:

constructor(private http: Http) { 

      //console.log(this.test);
      console.log(this.testing.getAPI);
      //this.test.load();
    }

I am getting the Console as Undefined.(Must be because my Service class is loading before API Class).

Thanks in Advance.

Mile answered 8/2, 2017 at 10:38 Comment(1)
if it comes to ts file you can simply import itNymphet
T
27

UPDATES

Inspired with the solution for this particular problem created ngx-envconfig package and published it on NPM registery. It has the same functionalities as it is provided in this answer and even more.


You can have the JSON file somewhere in assets folder like: assets/config. Depending on whether the environment is dev or not you can use two .json files, one for development and one for production. So you can have development.json and production.json files, where each one will keep the appropriate API endpoints.

Basically you need to go through the following steps:

1. Setting up environment (skip this step if you have it already)

Create two files in src/environments folder:

environment.prod.ts

export const environment = {
  production: true
};

environment.ts

export const environment = {
  production: false
};

2. Create JSON config files

assets/config/production.json

{
  "debugging": false,

  "API_ENDPOINTS": {
    "USER": "api/v1/user",
    ...
  }
}

assets/config/development.json

{
  "debugging": true,

  "API_ENDPOINTS": {
    "USER": "api/v1/user",
    ...
  }
}

3. Create a service as follows

Note depending on the environment, the ConfigService will load the appropriate file

import { Injectable, APP_INITIALIZER } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs';

import { environment } from 'environments/environment'; //path to your environment files

@Injectable()
export class ConfigService {

    private _config: Object
    private _env: string;

    constructor(private _http: Http) { }
    load() {
        return new Promise((resolve, reject) => {
            this._env = 'development';
            if (environment.production)
                this._env = 'production';
            console.log(this._env)
            this._http.get('./assets/config/' + this._env + '.json')
                .map(res => res.json())
                .subscribe((data) => {
                    this._config = data;
                    resolve(true);
                },
                (error: any) => {
                    console.error(error);
                    return Observable.throw(error.json().error || 'Server error');
                });
        });
    }
    // Is app in the development mode?
    isDevmode() {
        return this._env === 'development';
    }
    // Gets API route based on the provided key
    getApi(key: string): string {
        return this._config["API_ENDPOINTS"][key];
    }
    // Gets a value of specified property in the configuration file
    get(key: any) {
        return this._config[key];
    }
}

export function ConfigFactory(config: ConfigService) {
    return () => config.load();
}

export function init() {
    return {
        provide: APP_INITIALIZER,
        useFactory: ConfigFactory,
        deps: [ConfigService],
        multi: true
    }
}

const ConfigModule = {
    init: init
}

export { ConfigModule };

4. Integrate with app.module.ts

import { NgModule } from '@angular/core';
import { ConfigModule, ConfigService } from './config/config.service';

@NgModule({
    imports: [
        ...
    ],
    providers: [
        ...
        ConfigService,
        ConfigModule.init(),
        ...
    ]
})

export class AppModule { }

Now you can use ConfigService wherever you want get the necessary API endpoints defined in config .json files.

Tingaling answered 4/11, 2017 at 12:36 Comment(5)
thanks Karlen.this is more helpful.can u pls tell me,how can i access this config values in componentHartzel
@Hartzel you just need to use the ConfigService in your component and call the desired functions of the serviceTingaling
Great answer ! Please look at this if you want to upgrade to Angular 6 and rxjs 6 APIs : metaltoad.com/blog/angular-6-upgrading-api-calls-rxjs-6Hjerpe
In my case, the whole application is already loading values from some variables using AppConfiguration class. All other services have the AppConfiguration injected in constructor. So, I followed the above pattern and tried to set the values in AppConfiguration using the the service. However, the values won't load as other services try to inject the value before the AppConfiguration gets loaded from file. Any suggestions?Wraith
@Wraith have you used APP_INITIALIZER? You should force your application to wait for the config file, to do that you need to use APP_INITIALIZER like described above.Tingaling
N
9

In Angular 4+ projects generated with the Angular CLI, you will have the environment folder out-of-the-box. Inside of it, you will find the environment.ts files from Karlen's answer. That is a working solution for configuration with one caveat: Your environment variables are captured at build time.

Why does that matter? When you're setting up a CI/CD pipeline for your Angular app, you will generally have a build tool that builds your project (like Jenkins) and a deployment tool (like Octopus) that will grab that package (the dist folder) and deploy to the selected environment, replacing your environment variables with the correct values in the process. If you use the environment.ts files, your environment variables cannot be replaced this way because the environment.ts files do not get included in the dist folder. There is no file your deployment tool can pick up and edit.

What can we do? we can add a JSON configuration file inside of the assets folder. Those files are included by default in the dist folder we will want to deploy. When we want to use an environment variable, we simply import the settings like import config from '[relative/path/to/your/config/file.json]'.

When we do this, we will get something like the following error:

Cannot find module '../../config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension

This is because the typescript compiler tries to import an exported module and cannot find one. We can fix this by adding the following JSON properties/values in our tsconfig.json file.

"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,

resolveJsonModule allows the typescript compiler to import, extract types from, and generate .json files.

allowSyntheticDefaultImports allows default imports from modules with no default export.

With this in place, we can run our project and we will find that our error is gone and we can use our config values without any issues.

Now, because this config file is included in the dist folder that gets deployed on the server, we can configure our deployment tool to replace the variable values with the values specific to the environment to which we want to deploy. With this in place we can build our Angular app once and deploy it anywhere.

Another added benefit is that most deployment tools like Octopus ship with native JSON support so you can configure it to replace environment variables in your JSON file quite easily. The alternative is using a regex solution to replace environment variables in a .ts file, which is comparatively more complicated and prone to mistakes.

Noto answered 9/1, 2020 at 2:58 Comment(3)
i have followed your comments change but it wont works in some cases can you look into this question . the problem which i have is injected into auth service throws an error ,and in components which works fine #62769385Minium
Note: the config items above need to be in the compilerOptions section of tsconfig.json, and not the root section.Sacculate
I had to include: "esModuleInterop": true property on tsconfig.json to workBenavides
U
7

It is possible to import JSON in TypeScript. You need to add typings:

typings.d.ts:

declare module "*.json" {
  const value: any;
  export default value;
}

And then import like this:

import config from "../config/config.json";

config.json:

{
  "api_url": "http://localhost/dev"
}
Ucayali answered 6/6, 2017 at 19:29 Comment(2)
how do you call this in file? tells me can't resolve api_url... angular5Choctaw
try doing import * as configuration from '../config/config.json'; and use (<any>configuration)['api_url'];Ucayali
D
2

I had same issue and in the end i give up from .ts and put it in .js :D like this:

configuration.js in root

var configuration = {
    'apiHost': 'http://localhost:8900',
    'enableInMemoryWebApi': false,
    'authMode': 'standalone',
    'wsUrl': 'ws://localhost:8900/ws'
};

module.exports = configuration;

in .ts file for ex. user.service.ts

let configuration = require('../configuration'); //in import section
@Injectable()
export class UserService {
    ...
    getUser(id: number | string): Promise<User> {
        console.log(configuration.apiHost) //will get propertye from .js file
        return this.http.get(`${configuration.apiHost}/${id}`, this.headers).toPromise().then(this.extractData).catch(this.handleError);
    }
}

Hope it helps

Derrickderriey answered 8/2, 2017 at 11:9 Comment(3)
some errors? how did you put it? where did you put it?Derrickderriey
Add typing.d.ts in main folder of the application and over there declare the varible which you want to use every time. As for example: declare var System: any; declare var require: any;Derrickderriey
I tried this and working fine.But json changes doesn't give proper result after moving build.pls help meHartzel
J
0

You can use Opague token to set constant values as providers

Try: In your const file:

import { OpaqueToken } from '@angular/core';

export const CONFIG_TOKEN = new OpaqueToken('config');
export const CONFIG = {
  apiUrl: 'myUrl'
};

In your AppModule set to make it a singleton provider for the app:

providers:[
//other providers,
{provide: CONFIG_TOKEN, useValue: CONFIG}
]

For injecting in constructor,

constructor( @Inject(CONFIG_TOKEN) private config)
Journal answered 8/2, 2017 at 11:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.