8

I want to import a Json file which is in assets folder where I have below urls:

config.json:

{
    "url1": "https://jsonplaceholder.typicode.com/posts",
    
    "url2" : "https://reqres.in/api/users",
    
    "url3":"https://fakerestapi.azurewebsites.net/api/Authors"
}

So instead of hard coding the URL, I want to import from Json file, but I am not sure how to do that exactly.

Any suggestions or scenarios would be appreciated, below are my issues:

1. How to import Json file to environment.ts and from there I will have a service which consumes the api

2. If I import the file, it needs to be the same for prod and loc dev also

what I want :

I have a config file where it contains some URL's in .json file stored in asset folder now instead of loading environments.prod or .ts, I want to load my Json file config and basing on that I want to run my application

what I did:

Below is my Json file which I placed in asset folder

{
    "baseUrl": "https://jsonplaceholder.typicode.com/",
    "baseUrl2": "https://reqres.in/api/users"
}

ConfigServiceService.ts for storing config file

public _config: Object;

    constructor(public http:Http) { }
    
    getData(){
       debugger;
       return this.http.get("./assets/config.json").pipe(map(res =>  res.json()));
    }

After this, I create a ServiceProviderService.ts for calling the service file

configData:any;


   constructor(public http:Http,public config:ConfigServiceService) {
    
   }
    
   jsonData(){
       debugger;
       return this.configData;
   }
    
   ngOnInit(){
      debugger;
      this.config.getData().subscribe(res =>{
         console.log(res);
         this.configData = res;
      });
    
    
   }

app.component.ts

 title = 'sample';
 constructor(public serv :ServiceProviderService){
      this.serv.jsonData();
 }

I am not able to get the Json data and if I am putting the logic which is there is ngOnInit in ServiceProviderService.ts file if I put it in constructor then I am getting undefined.

Note : here if there are more than once url then each url is distributed to various seperate service file suppose base1 url for 1 service file ans base2 url for another file how can I achieve that

https://stackblitz.com/edit/read-local-json-file-5zashx

in app.component.ts im getting undefined

enter image description here

Community
  • 1
  • 1
Dexter
  • 518
  • 3
  • 12
  • 40

8 Answers8

17

Since you have static strings in a JSON file, which is already saved in /assets folder and it's not on the server, you don't need to use http.get.
We use http protocol to get data from JSON files from the server/backend only, not from the client side folders.
You just need to import the JSON file wherever you need (even in environment.ts), as you would do with DTOs. Use Typescript's ES6 import statement combined with export in the JSON file.

assets/config.ts

export const URLS = Object({

     "url1": "https://jsonplaceholder.typicode.com/posts",

     "url2" : "https://reqres.in/api/users",

     "url3":"https://fakerestapi.azurewebsites.net/api/Authors"
})

Then anywhere (components, directives, services, classes, interfaces, etc ...)

import { URLS } from 'assets/config.ts';

....

this.url1 = URLS.url1;

//or

let url1 = URL.url1;

Now this.url1 can be used in any api call inside http.get, for example:

 this.http.get(`${this.url1}`, { headers: this.getHeaders(token) });

or

 this.http.get(`${url1}`, { headers: this.getHeaders(token) }) // where url1 could be a variable or method parameter, etc...

That's it

Vega
  • 27,856
  • 27
  • 95
  • 103
  • 2
    This is the best answer, I have seen a lot of questions on SO and most of the developers are making their lives harder. Good job @Vega **KISS principle** – George C. Mar 28 '19 at 16:34
  • If you do it like this, if you update the JSON file, you would have to restart the server to pick up the changes, is that correct? You will still have to use Http.gets if you want the option to change these settings at running time, I presume. @Vega – Narshe Aug 19 '19 at 14:17
  • 1
    @Narshe, in fact you can update the JSON file only when you update the build, so indirectly it would need the server to restart, as you restart it with each update. You don't need http.get to get these values at any time, there are like DTOs. Hope it's clear enough – Vega Aug 19 '19 at 15:09
  • 1
    Thanks for the quick and clear response. I've also been trying to get the solution on my own and these were exactly my findings. Unfortunately I think I may need to rely on HTTP calls or similar solution. My requirements dictate that config would require at maximum a server restart and by using this solution I will still have to rebuild.Thanks again :) – Narshe Aug 19 '19 at 15:37
  • Is it possible to define a type for the config instead of using Object? – Rookian Aug 20 '20 at 11:24
1

You can create a service to read Json file using HttpClient

export class SettingService  {

  constructor(private http: HttpClient) {

  }

  public getJSON(file): Observable<any> {
      return this.http.get("./assets/configs/" + file + ".json");
  }
  public getSetting(){
      // use setting here
  }
}

You should save 3 url to session storage or localstorage instead of enviroment.ts incase you refresh page

Refer Angular: Is it possible to read a json file after building

Hien Nguyen
  • 24,551
  • 7
  • 52
  • 62
  • if we put in environment.ts is ther any issue why im askng bcoz as i have 3 types of urls so i want to use 3 url for 3 diff services from environment – Dexter Mar 21 '19 at 16:53
  • yes, you can use both 3 diff url, but you should keep url in session storage or localstorage for refreshing page – Hien Nguyen Mar 21 '19 at 16:56
  • ok what u suggested is good so what ur view for best practice in service or environment and saving url in storage is not good thing so ur first approach is better – Dexter Mar 21 '19 at 16:59
  • so even thought it is prod or dev environment the url wont change right i mean it work right – Dexter Mar 21 '19 at 17:01
  • you can configure it to prod url version, my project do like this. – Hien Nguyen Mar 21 '19 at 17:05
  • if i mention it in service file then there is no need to mention in environment – Dexter Mar 21 '19 at 17:07
  • yes, we did not use environment file because after build, user cant change backend url – Hien Nguyen Mar 21 '19 at 17:09
  • ok sure let me give a try could specify the whole process for that how to ? – Dexter Mar 21 '19 at 17:10
  • could please check the stackblitz i tried the approach but getting undefined in app.component.ts https://stackblitz.com/edit/read-local-json-file-5zashx – Dexter Mar 24 '19 at 15:55
  • I checked it show base url value? https://stackblitz.com/edit/read-local-json-file-ip4k1t – Hien Nguyen Mar 25 '19 at 00:36
  • please check this https://stackblitz.com/edit/read-local-json-file-ip4k1t here instead of reading from config service i wan to read it from the settings service when i do that getting undefined – Dexter Mar 25 '19 at 03:51
1

You can use required to read a json file from assets

/assets/urls.json

{
  "urls": {
    "dev": "dev.com",
    "prod": "prod.com"
  }
}

env.ts

const { urls } = require("../assets/urls.json");

export const environment: IEnv = {
  production: false,
  baseUrl: urls.dev
};

env.prod.ts

const { urls } = require("../assets/urls.json");
export const environment: IEnv = {
  production: true,
  baseUrl: urls.prod
};

you need to install npm i @types/node and update tsconfig.app.json like this

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "types": ["node"]
  },
  "exclude": ["test.ts", "**/*.spec.ts"]
}
Muhammed Albarmavi
  • 23,240
  • 8
  • 66
  • 91
0

You can create constants for all these urls in environment file. I do not see a scenario in your case to put it in a separate json file. For prod, you can use the same environment file by changing default configurations property in angular.json

0
    import { environment } from '../../../environments/environment';

    domain :string = environment.domain;

    if(!!environment) {
        if(environment.qa) {
          "url1": "https://jsonplaceholder.typicode.com/posts",
        }
        if(environment.dev) {
           "url2" : "https://reqres.in/api/users",
        } 
      }

you can import the baseUrl property in the asset folder to your service file and execute as I mentioned above.

https://alligator.io/angular/environment-variables/

Please find the url for detailed explanation of environment variables in angular

JayK
  • 73
  • 1
  • 9
0

So, I would create a node script called populatedDynamicUrls.js that can read those values IN from the json file, and write those values OUT to the corresponding environment.ts file for that specific environment. THEN your angular build command would look like this: node populateDynamicUrls.js && ng run build, and your angular serve command would look like this: node populateDynamicUrls.js && ng run serve

Doug S.
  • 682
  • 1
  • 10
  • 26
0

If you want to import json file

  1. Add config.json to the assets directory.
  2. Edit typings.d.ts file:
    declare module "*.json" {
        const value: any;
        export default value;
    }
  1. Add path of typings.d.ts to "typeRoots" in tsconfig.json, for example:
"typeRoots": [
      "node_modules/@types", "./src/typings.d.ts"
],
  1. Import config.json in environment file:
import * as config from '../assets/config.json';

export const environment = {
  baseUrl: config.default.url1
  production: true
};

That's all, good luck!

Yakir Tsuberi
  • 1,373
  • 1
  • 12
  • 16
0

I'm not sure if this is the best solution, but here's what I did (by cut'n'pasting what was already in the default Angular project). I had the same goal: I wanted an "API URL" to be available, depending on which environment (Debug or Production) we were using.

First, I added an extra config setting to the two existing .ts files:

  • src\environments\environment.prod.ts
  • src\environments\environment.ts

..so now, each of the files looked something like this:

export const environment = {
  production: false,
  apiUrl: "http://localhost:53104"
};

The main.ts file already has code to read in the environment object, and it uses the environment.production value, so I just added a bit of code to this file to do the same with the environment.apiUrl value:

export function getAPIUrl() {
  console.log("API URL: " + environment.apiUrl);
  return environment.apiUrl;
}

const providers = [
  { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] },
  { provide: 'API_URL', useFactory: getAPIUrl, deps: [] }
];

. . . 

platformBrowserDynamic(providers).bootstrapModule(AppModule)
   .catch(err => console.log(err));

With this in place, I can access this new "API_URL" value in my components.

For example:

export class FetchDataComponent {
  public forecasts: WeatherForecast[];

  constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string, @Inject('API_URL') APIUrl: string) {
    http.get<WeatherForecast[]>(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => {
      this.forecasts = result;
    }, error => console.error(error));

    console.log("FetchDataComponent, URL: " + APIUrl);
  }
}

When I open this page, I can see my URL shown in the Output window.

FetchDataComponent, URL: http://localhost:53104

Is this solution okay ? It seems a lot simpler (with almost no code) than the other solutions.

Actually, an even simpler solution is to not use a "API_URL" provider, and just stick this in your components:

import { environment } from '../../environments/environment';

..then just access the value using:

console.log("FetchData, URL: " + environment.apiUrl);  

Job done !

Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159