29

I need to pass the backend url to my Angular2 app, since production and dev servers are hosted on different locations.

I know I could store such things in an external config.json and load upon startup. This, however, seems like unnecessary extra call to server before the app can get started.

Alternatively, what I do now is I do create a single global variable, which I inject in gulp depending on build. My app isn't a lib that needs to be reusable I don't believe I should hit unexpected global name clashes. But it's not a good practice.

I wonder if there's a third, better solution?

4
  • 2
    IMO, you're over-optimizing. I'll take a single "unnecessary" get in order to implement a well-understood pattern (like loading such things from a config file) every time. Commented Apr 10, 2016 at 11:17
  • You're possibly right, especially when Angular 2 hits production and the production builds and minification starts working really good. Thanks. Commented Apr 10, 2016 at 12:12
  • If using angular-cli, easy solution to this described here: stackoverflow.com/questions/40424199/… Commented Mar 7, 2017 at 18:06
  • If you have to build once and deploy the same build artifact multiple times, see stackoverflow.com/a/43980985/2540679 Commented May 18, 2017 at 1:16

7 Answers 7

17

Just put variables inside the environment object.

export const environment = {
  production: false,
  url: 'http://localhost:3000/api'
};

After that, you could import and use it. Angular Cli will swap files automatically.

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

P.S. Variables are available in environment object.

this.url = environment.url
Sign up to request clarification or add additional context in comments.

3 Comments

this works very well , from within any component/service/pipe , however how to access those variables from the main app.js ? console.log(process.env.anotherVariable) is undefined ...
when I tried it I get the error 'cannot find name process'
Anyway a simpler solution with angular-cli is described here: stackoverflow.com/questions/40424199/…
11

I would see two ways to do that:

  • Leverage a JSON configuration in a file. This file would be loaded before boostrapping the application:

    var app = platform(BROWSER_PROVIDERS)
       .application([BROWSER_APP_PROVIDERS, appProviders]);
    
    var http = app.injector.get(Http);
    http.get('config.json').subscribe((config) => {
      return app.bootstrap(AppComponent, [
        provide('config', { useValue: config })
      ]);
    }).toPromise();
    

    Here is a corresponding plunkr describing the global approach: https://plnkr.co/edit/ooMNzEw2ptWrumwAX5zP?p=preview.

  • Leverage a configuration module:

    export const CONFIG = {
      (...)
    };
    

    that will be imported and included in providers when bootstrapping the application:

      import {CONFIG} from './config';
    
      bootstrap(AppComponent, [
        provide('config', { useValue: CONFIG })
      ]);
    

With the two approaches, configuration can be defined for each environment when packaging the application.

This question could also give you hints about how to package an Angular2 application:

1 Comment

Well, the first answer I've described already. For the third one, how would you set the CONFIG variables based on the environment I'm building for?
6

I have been struggling with the above for a while too. And even though Thierry's answer is very good, but I somehow find it too complicated for simply having env vars, so I thought I present my thoughts too.

1) I first created an interface describing my env vars app.settings.ts:

export interface IEnvVars {
    ENV: string;
    API_URL: string;
}

export class AppSettings {
    public static env_vars: IEnvVars;
}

2) I added the JavaScript fetch API type definitions to my .d.ts file. (Not absolutely needed if you don't mind having fetch as unresolved, but I did mind it, so added the types)

3) So now in my main.ts I can make a call to the endpoint that returns the environment variables:

import {bootstrap}    from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {AppSettings,IEnvVars} from "./configs/app.settings";
import {enableProdMode} from "angular2/core";

fetch('/env-vars', {method: 'get'}).then((response) => {
    response.json().then((env_vars:IEnvVars) => {
        AppSettings.env_vars = env_vars;
        if (AppSettings.env_vars.ENV != 'development') enableProdMode();
        bootstrap(AppComponent);
    });
});

So now I access my env vars in any service or component simply by:

import {AppSettings} from "../configs/app.settings";

so within a method etc. you could go like:

let customersURL = `${AppSettings.env_vars.API_URL}/customers`;

Please note that you may need polyfills for fetch API as Safari and IE still don't support this. (Link to polyfill exampel)

3 Comments

Thanks, but the question was about how do you set the /env-vars endpoint. (Without having access to backend.) I'm going with gulp build step now.
@Zlatko /env-vars is an endpoint that contains your settings or could be just a pure json file /env-vars.json located within your porject.
There is an issue github.com/angular/angular-cli/issues/3540 that you might face using such approach so you need to use the work around github.com/angular/angular-cli/issues/… until angular-cle getitg it fixed.
3

Not only the URL but there are some other parameters that need to be taken from environment variables.

Environment variables must be taken and written to environment.ts before build! because of some Javascript restrictions in accessing filesystem, environment, etc at runtime.

For this, first you need a module for writing environment variables and this module has to be executed with ts-node in your build script:

"scripts": {
  .....
  "config": "ts-node set-env.ts",
  "build": "npm run config && ng build",
  ....
},

and your set-env.ts file:

var fs = require('fs');
const targetPath = './src/environments/environment.ts';
const colors = require('colors');
require('dotenv').load();
const envConfigFile = `export const environment = {
  ENV_VAR_1: '${process.env.var_1}',
  ENV_VAR_n: '${process.env.var_n}',
};`;
console.log(colors.magenta('The content of `environment.ts` will be: \n'));
console.log(colors.grey(envConfigFile));
fs.writeFile(targetPath, envConfigFile, function (err) {
  if (err) {
    throw console.error(err);
  } else {
    console.log(colors.magenta(`environment.ts file is created`));
  }
});

and of course you have to add ts-node and dotenv to your dependencies.

and you can easily access variables of environemnt.ts like:

import {environment} from '../environments/environment';
....
var envVar1 = environment.ENV_VAR_1;

Node 12^, Angular 10^, and I didn't check this with older Node/Angular/Typescrip versions

Comments

2

I think this might be useful to you. Passing asp.net server parameters to Angular 2 app

This way you can inject any value from html page (which can get value from the server, like current environment variable)

Comments

1

I have implemented this using the following approach:

  1. Created ts files for each environment.
  2. Modified package.json and added a new entry for every environment in scripts section.

for eg:-

"start:local": "copy \"config/config.local.ts\" \"constants/global.constants.ts\" && npm run start"

  1. Import global.constants into your app.
  2. You can start your HTTP server using : npm run start:local

1 Comment

Do you have any "devDependencies" to make that copy command work? I get an error, The system cannot find the file specified. 0 file(s) copied. I thought it was a pathing issue but In Visual Studio Code, I'm allowed to Ctrl+click the file and it is indeed there.
-1

In different environments, the value of location.host will be different.

Use this as the key and load the values from the corresponding json having all the URLs.

The relevant code may look like this:

let env = location.host;
if (env === 'prod.example.com') {
  config = 'ProdConfig.json';
} else if (env === 'test.example.com') {
  config = 'TestConfig.json';
} else if (env === 'localhost') {
  config = 'DevConfig.json';
}

1 Comment

Not in my case. Let's say I am running my local, dev env. And I have locally the API. But a colleague doesn't have the backend installed locally (databases, all the setup, he doesn't need it) - he'll point the backend-url to our sandbox API. That's why I don't want to resolve this from the app itself, but rather at build time.

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.