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?

Zlatko
  • 18,936
  • 14
  • 70
  • 123
  • 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. – drew moore Apr 10 '16 at 11:17
  • You're possibly right, especially when Angular 2 hits production and the production builds and minification starts working really good. Thanks. – Zlatko Apr 10 '16 at 12:12
  • If using angular-cli, easy solution to this described here: http://stackoverflow.com/questions/40424199/angular-2-testing-process-env – rmcsharry Mar 07 '17 at 18:06
  • If you have to build once and deploy the same build artifact multiple times, see http://stackoverflow.com/a/43980985/2540679 – Mehrad Sadegh May 18 '17 at 01:16

8 Answers8

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
Boris Siscanu
  • 1,009
  • 7
  • 13
  • 1
    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 ... –  Nov 22 '16 at 12:03
  • when I tried it I get the error 'cannot find name process' – rmcsharry Mar 07 '17 at 17:50
  • Anyway a simpler solution with angular-cli is described here: http://stackoverflow.com/questions/40424199/angular-2-testing-process-env – rmcsharry Mar 07 '17 at 18:06
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:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • 3
    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? – Zlatko Apr 10 '16 at 12:58
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)

makeitmorehuman
  • 11,287
  • 3
  • 52
  • 76
  • 1
    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 May 03 '16 at 08:29
  • @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. – angularrocks.com Feb 01 '17 at 04:35
  • There is an issue https://github.com/angular/angular-cli/issues/3540 that you might face using such approach so you need to use the work around https://github.com/angular/angular-cli/issues/3540#issuecomment-275853502 until angular-cle getitg it fixed. – angularrocks.com Feb 01 '17 at 04:37
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

void
  • 7,760
  • 3
  • 25
  • 43
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)

Community
  • 1
  • 1
ravi punjwani
  • 496
  • 4
  • 12
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
A J
  • 3,970
  • 14
  • 38
  • 53
Balaji
  • 11
  • 1
  • 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. – markreyes May 23 '17 at 00:37
-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';
}
Zameer Ansari
  • 28,977
  • 24
  • 140
  • 219
  • 1
    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. – Zlatko Apr 28 '17 at 09:30
-3

Environment.ts

How to use it

you have a builtin system for that in Angular2+.
you have environement.ts files for every environement you handle(you can define as much as you need).
Wehenever you need to access them, you import it like this

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

and in the code you access your variable

environement.varName

How to configure custom environement.ts

in your angular.json file, you can create as many environement as you want, here is an example

"build": 
{ "configurations": 
{ 
"production": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], }, 

"qa": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.qa.ts" } ] }, 

"staging": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.staging.ts" } ] } } }

when building your application you need to mention the name of the environement you build for. foe example here I made a build for qa environement.

ng build --configuration qa

once done, your dist output folder should contain build output with qa configuration

Ismail Diari
  • 498
  • 3
  • 8