I want to deploy a production build of angular app with a configurable api url for the user to test it out. I use the environment.ts but after the production build, I do not know how to configure the variables.
What approach needs to be done?
The environment*.ts file contain build time configurations, which you cannot change after the build. If you need to change your configuration after the build, you need to put them in a different place and retrieve them dynamically when the application starts
What you can do is:
Step #1: put your json configuration files under src/assets/config/[envName].json.
Note: it has to be json format, not ts format
Step #2: Add a new config service
import {Inject, Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {Observable} from 'rxjs/Rx';
import {environment} from "../../environments/environment";
/**
* Declaration of config class
*/
export class AppConfig
{
//Your properties here
readonly production: boolean;
readonly name: string;
readonly apiBaseUrl: string;
}
/**
* Global variable containing actual config to use. Initialised via ajax call
*/
export let APP_CONFIG: AppConfig;
/**
* Service in charge of dynamically initialising configuration
*/
@Injectable()
export class AppConfigService
{
constructor(private http: HttpClient)
{
}
public load()
{
return new Promise((resolve, reject) => {
let confName = environment.name + '.json';
this.http.get('/assets/config/' + confName).map(res => res as any).catch((error: any): any => {
reject(true);
return Observable.throw('Server error');
}).subscribe((envResponse :any) => {
let t = new AppConfig();
//Modify envResponse here if needed (e.g. to ajust parameters for https,...)
APP_CONFIG = Object.assign(t, envResponse);
resolve(true);
});
});
}
}
Step #3: In your main module, add this before declaring the module
/**
* Exported function so that it works with AOT
* @param {AppConfigService} configService
* @returns {Function}
*/
export function loadConfigService(configService: AppConfigService): Function
{
return () => { return configService.load() };
}
Step #4: Modify the module providers to add this providers: [ …
AppConfigService,
{ provide: APP_INITIALIZER, useFactory: loadConfigService , deps: [AppConfigService], multi: true },
],
Step 5: In your code, instead of using environment.configXXX, use this
import {APP_CONFIG} from "../services/app-config.service";
//…
return APP_CONFIG.configXXX;
This is a simplified example, you'll actually need to make some changes if you use angular universal as you need to have absolute urls when making an http call
Are you using Angular-CLI? It should be easy, then. You have something like this:
src/
app/
environment/
environment.ts
environment.prod.ts
Simply put different url in environment.prod.ts
and your prod build gets a second url. E.g. let's say your environment.ts
looks like this:
{
"production": false,
"apiUrl": "http://localhost:8080"
}
Put this in environment.prod.ts
:
{
"production": true,
"apiUrl": "https://example.com/api"
}
You can setup more environments, check that section of .angular-cli.json
and angular-cli repo.
Edit: As per your comment, you want more.
Yes but still this is not configurable after the build isn't it? Because I don't know what url the user want to use therefore I want to make it configurable from the outside after deploying the build.
Let's continue this scenario further. Let's have a backend client:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { apiUrl } from '../environment/environment.ts';
@Injectable()
export class BackendService {
backendUrl: string = apiUrl;
constructor(private httpClient: HttpClient) {}
get(endpoint: string, params: any): Observable<any> {
const url= `${this.backendUrl}/${endpoint}`;
return this.httpClient.get(url, params);
}
}
Simplified, but works. By default, you set your own URL. But your components can set the url on the fly, and get other things from that url.
Now, the next step would be, offering which backends you have. This can be a preconfigured array, or you can let the client enter the url freely (simply input box). You can have a component that does that, and configures this service here. You should probably also have a separate service for your "proper" backend, where, e.g. your auth lies. But this all really depends on your scenario.
Well unless we need some async call to get them. Otherwise I would suggest this:
env.js
(function (window) {
window._env = window._env || {};
window._env.url= 'http://api-url.com';
}());
index.html
<head>
<script src="env.js"></script>
</head>
<body>
<app-root></app-root>
</body>
Finally add it in angular.json
"assets": [
"any/env.js",
Now you can just read the window using a service in your app
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With