Angular js read environmental variables - angularjs

I am trying to read environment variables in http get calls
$http.get('http://' + $location.host() + ':8080/api')
I want to be able to read the environmen variable and use it as the http rest server in teh above API call, as follows
$http.get('environmental_variable ':8080/api')
Note: I dont know the environment variable until runtime So I cannot have the value before hand to use it as a constant

There are lots of examples showing how you can put your settings into different files or constants. Most of these work, but miss the point.
Your configuration settings are not part of your code!
Apart from the 'Hello World' examples, your deployment should be carried out by a CI/CD server and this should be responsible for setting your configuration settings. This has a number of benefits:
1) You are deploying the same code to different environments. If you deploy code to a test environment, then you want to deploy the same code to your production environment. If your servers have to rebuild the code, to add the production configuration settings, you are deploying different code.
2) Code can be shared without giving away your API details, AWS settings and other secret information.
3) It allows new environments to be added easily.
There are lots of examples out there on how to do this. One example is www.jvandemo.com/how-to-configure-your-angularjs-application-using-environment-variables

There are no such things as environment variables in the browser.
The $location service is always going to get your current URL. I guess your API might live on a different host.
It's possible to simulate environment variables by storing configuration in an Angular constant.
app.constant('env', {
API_URL: "http://someurl.com:8080/api"
});
Then you can inject this constant into your other providers.
app.controller('MyController', function($http, env) {
$http.get(env.API_URL);
});
Here's a decent article on best practices with constants. The article favours not using constants, as it's useful to be able to modify the configuration without having to rebuild the code.
The way I normally handle this is to move all the instance configuration details out to a config.json file, then load it with $http when I bootstrap my application.
For instance, you might have a config file like this.
{
apiUrl: "http://someurl.com:8080/api"
}
Then an Angular service that loads it.
app.service('Config', function($http) {
return function() {
return $http.get('config.json');
};
});
Then other services can get hold of the promise, that will expose the config when resolved.
app.controller('MyController', function($http, Config) {
Config()
.then(function(config) {
$http.get(config.apiUrl);
});
});

I strongly suggest you to use a library for setting environment variables. You can use angular-environment plugin to do that: https://www.npmjs.com/package/angular-environment
Here's a example
angular.module('yourApp', ['environment']).
config(function(envServiceProvider) {
// set the domains and variables for each environment
envServiceProvider.config({
domains: {
development: ['localhost', 'acme.dev.local'],
production: ['acme.com', '*.acme.com', 'acme.dev.prod'],
test: ['test.acme.com', 'acme.dev.test', 'acme.*.com'],
// anotherStage: ['domain1', 'domain2']
},
vars: {
development: {
apiUrl: '//api.acme.dev.local/v1',
staticUrl: '//static.acme.dev.local',
// antoherCustomVar: 'lorem',
// antoherCustomVar: 'ipsum'
},
test: {
apiUrl: '//api.acme.dev.test/v1',
staticUrl: '//static.acme.dev.test',
// antoherCustomVar: 'lorem',
// antoherCustomVar: 'ipsum'
},
production: {
apiUrl: '//api.acme.com/v1',
staticUrl: '//static.acme.com',
// antoherCustomVar: 'lorem',
// antoherCustomVar: 'ipsum'
},
// anotherStage: {
// customVar: 'lorem',
// customVar: 'ipsum'
// },
defaults: {
apiUrl: '//api.default.com/v1',
staticUrl: '//static.default.com'
}
}
});
// run the environment check, so the comprobation is made
// before controllers and services are built
envServiceProvider.check();
});
Then you can get the right variable based on what environment your application is running:
var apiUrl = envService.read('apiUrl');
Cheers!

Related

Using different domain for development and production in angularjs

I'm working on AngularJS single page application that consume REST services. The front-end application is developed separately from the back-end and therefore during development we've to hardcode the domain name in the URLs for AJAX calls (we've enabled CORS). But in the case of production everything is running in the same domain and hence hardcoding the domain name looks little bad. Is there we can use the domain name in urls for ajax calls during development and in production don't hardcode the domain name? I'm using gulp.
An example of using gulp-ng-constant with an $http interceptor:
The following task in gulpfile.js will generate the file target/build/scripts/_config.js with the contents angular.module('globals', []).constant('apiContextPath', '...');:
gulp.task('app.ngconstant', [], function() {
var ngConstant = require('gulp-ng-constant'),
var rename = require('gulp-rename');
return
ngConstant({
constants: {
apiContextPath: '.' // TODO Devise a way to set per environment; eg command line
},
name: 'globals',
stream: true
})
.pipe(rename('_config.js'))
.pipe(gulp.dest('target/build/scripts'));
});
Obviously you need to include the generated file in your (packed/minified) code.
Something along the following code will configure the $httpProvider to prepend the apiContextPath to all requests that start with '/api/' (i.e. our REST endpoints):
angular.module(...).config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['globals', function(globals) {
return {
'request': function(config) {
if( config.url.indexOf('/api/') === 0 ) {
config.url = globals.apiContextPath + config.url;
}
return config;
}
};
}]);
}]);
(There are quite a few other configuration options, so this is just an example from an older project I worked on.)
If you don't already, you could pass your gulp build a parameter to build in production or development mode.
Based on that flag you can set a baseUrl property in your gulpfile that is used to include a script (using gulp-insert) into your javascript build before all of your angular scripts:
'window.baseUrl = ' + baseUrl
Then you could have a constant in your application that your services use to get the baseUrl:
angular.module('YourModule').constant('baseUrl', window.baseUrl);

How to get config values from server in Angular?

My application needs some config values on application startup. Suggestions from the community is to store them as constant as separate module, preferably in separate .js file. This might work for me.
However my configuration values are also stored on the server, and dont want to duplicate those on client side, so i was thinking of making server call to get those.
Im newbie to angular, is it valid design practice to make server call in module's config method? If yes then should i just use $http service to get the values from the server?
var main = angular.module('myapp', ['AdalAngular']);
main.config(['$stateProvider',$httpProvider, adalAuthenticationServiceProvider', function ($stateProvider,$httpProvider,adalProvider) {
// $stateProvider configuration goes here
// ?????CAN I make server call here to get configuration values for adalProvider.init method below???
adalProvider.init(
{
instance: 'someurl',
tenant: 'tenantid',
clientId: 'clientid',
extraQueryParameter: 'someparameter',
cacheLocation: 'localStorage',
},
$httpProvider
);
}]);
main.run(["$rootScope", "$state", .....
function ($rootScope, $state,.....) {
// application start logic
}]);
main.factory("API", ["$http", "$rootScope", function ($http, $rootScope) {
// API service that makes server call to get data
}]);
EDIT1
So based on suggestions below I'm going with declaring constant approach. Basically I will have separate config.js file and during deployment process I will overwrite the config.js file with respective environment based config.js file.
Question
If have to 10 constants then i have to pass them separately to module.config(). Is it possible to declare constant value as JSON object and somehow read it in config function so I don't have pass 10 different parameters?
angular.module('myconfig', [])
.constant('CONFIGOBJECT','{Const1:somevalue,Const2:somevalue,Const3:somevalue,Const4:somevalue}');
and then how do I read the values in config method?
var main = angular.module('myapp',['myconfig']);
main.config(['CONFIGOBJECT',function(CONFIGOBJECT){
?? How do I read CONFIGOBJECT value that is a string not json object?
})
I'll describe the solution used in project that i was working on some time ago.
It's true that you cannot use services in config phase, and it's also true, that you can use providers and constants while config phase.
So we used the next solution:
firstly, we created simple object with config, like
var config = {
someConfig1: true,
someConfig2: false,
someConfigEvents: {
event1: 'eventConfig1',
event2: 'eventConfig2'
}
etc...
}
Then we also declared angular value with jQuery lib:
app.value('jQuery', jQuery);
And now we cannot use services like $http, but we can use jQuery, so we just making ajax call to config server and extending our config:
jQuery.ajax("path/to/config", { async: false, cache: false })
.done(function (data) {
var response = angular.fromJson(data)
if (response) {
angular.extend(config, response.returnData.data);
} else {
alert('error');
}
})
.fail(function () {
alertError();
})
.always(function () {
appInit();
});
You cannot inject a service into the config section.
You can inject a service into the run section.
So, you cannot use - for example $http service to retrieve data from server inside config() , but you can do in inside run(), which initializes the provider's service.
See also more complete answer here.
Hope this helps.
UPDATE:
Why string? Why don't you simply use
.constant('CONFIGOBJECT', {Const1:somevalue,Const2:somevalue,Const3:somevalue,Const4:somevalue}
for
.constant('CONFIGOBJECT', '{Const1:somevalue,Const2:somevalue,Const3:somevalue,Const4:somevalue}'
?
Only providers are available during the config phase, not services. So you can't use $http during this phase.
But you can use it during the execution phase (in a function passed to run()).
An alternative is to have some JavaScript file dynamically generated by the server, and defining the constants you want.
Another alternative is to generate such a JS file during the build, based on some file that would be read by the server-side code.

Angular, without a browser?

Sometimes I want to experiment (in a local Node script) with some aspect of Angular - e.g. Services, DI, etc - stuff that has nothing to do with the Browser or the DOM. Is there a way to do that? i.e. load some base portion of the Angular Infrastructure? If I just require("angular") in a Node script, it complains:
ReferenceError: window is not defined
which makes sense because Angular lives for the Browser-window.
But it seems like some portions of Angular could be used for non-web applications - although that's not my reason for asking this. I'm just trying to improve my understanding Angular and sometimes want to do a little experiment while stripping away/ignore as much as possible.
Experimenting with Angular is best done in a browser, due to window and other API's Angular relies on.
However, if you're dead set on using Angular with node, you might look into the vm module which essentially lets you eval code with a specific stand-in object as a sort of proxy global object. e.g.:
const vm = require('vm');
const fs = require('fs');
const test = fs.readFileSync('./test.js', 'utf-8');
const windowProxy = {
document: {
createElement: function() {
return {
setAttribute: function() {},
pathname: ''
}
},
querySelector: function() {},
addEventListener: function() {}
},
location: {
href: ''
},
addEventListener: function() {}
};
windowProxy.window = windowProxy;
vm.createContext(windowProxy);
vm.runInContext(test, windowProxy);
will at least let you load Angular without complaining. Undoubtedly you would encounter more errors, and would have to polyfill the missing browser API's yourself.
You might also look into PhantomJS for a more robust testing environment, though that would no longer be Node.

Off-line Chrome Packaged App: how to manage Prod and Dev modes

To switch between dev/stage/prod on the server, I set an ENV variable. This is pretty standard.
With an Off-line Chrome App, how do I switch between dev/stage/prod? Especially around REST API URL's?
During development my app is installed in chrome as an "unpacked" app.
SOLUTION:
I combined these answers. Here's what I did:
On install, if unpacked extension, I set a value in localStorage.
On app run, I set a variable to the localstorage value, or to production if undefined.
FWIW, here's the code:
background.js:
chrome.runtime.onInstalled.addListener(function () {
console.log('onInstalled');
// Note: this event is fired every time the "Reload" link is clicked in
// 'Chrome Apps & Extensions Developer Tool'. So only set if not set.
// If unpacked extension,
if(!chrome.runtime.getManifest().update_url) {
// Load existing value
chrome.storage.local.get('APIBaseURL', function(data) {
// Has value already been set?
if (!data.hasOwnProperty('APIBaseURL')) {
// set API server to localhost
chrome.storage.local.set({'APIBaseURL': DEV_APIBASEURL }, function() {
// Ok, notify the console.
console.log('Installed in dev mode: APIBaseURL = '+DEV_APIBASEURL);
} );
}
});
}
});
App.js (this is Angular, but you should see the pattern. Promises are ES6)
var PROD_APIBASEURL = 'https://production.com';
angular.module('wmw', ['wmw.routes'])
// Listen for online/offline events and set status in $rootScope
.run(['$rootScope', function($rootScope){
// Determine which server to run on
$rootScope.isDev = chrome.runtime.getManifest().hasOwnProperty('update_url');
// Async init code is in a Promise
$rootScope.promiseAppReady = new Promise(function(resolve, reject) {
// Get the Base URL
chrome.storage.local.get('APIBaseURL', function(data) {
// Apply it to our scope. If not set, use PROD.
$rootScope.$apply(function() {
if (data.hasOwnProperty('APIBaseURL')) {
$rootScope.APIBaseURL = data.APIBaseURL;
} else {
$rootScope.APIBaseURL = PROD_APIBASEURL;
}
resolve($rootScope.APIBaseURL);
});
});
});
}]);
$rootScope.promiseAppReady let's me know when the code is done and the app is ready.
$rootScope.$apply() bubbles changes up to other scopes. If you're not using Angular, you can remove this.
I also included this code with some debug tools:
var debugTools = {
setAPIBaseURL: function (url) {
chrome.storage.local.set({'APIBaseURL': url});
},
showAPIBaseURL: function() {
chrome.storage.local.get('APIBaseURL', function(data) {console.log(data)});
}
}
so it was easy to change in the console.
In the console chrome.runtime.getManifest().update_url will have a value if installed from the store. Undefined if not.
See How to distinguish between dev and production in a Firefox extension I'm building?
And Check if Chrome extension installed in unpacked mode
From your description, I don't think you want the Chrome App to only talk to the remote server when it's installed from the Chrome Web Store and only talk to the local server when it's installed unpacked. I would think that you'd want the option of talking to either server no matter how it's installed.
So, I'd program the app to choose its server based on a key in Local Storage. You can then easily set that key from the Developer Tools panel (the Resources) tab. If the key is undefined, it uses the remote server.

Loading relative templateUrl

I've been trying to find the best way to create a modular, scalable angular application. I really like the structure of projects like angular-boilerplate, angular-app, where all the related files are grouped together by feature for partials and directives.
project
|-- partial
| |-- partial.js
| |-- partial.html
| |-- partial.css
| |-- partial.spec.js
However, in all these examples, the template URL is loaded relative to the base url, not relative to the current file:
angular.module('account', [])
.config(function($stateProvider) {
$stateProvider.state('account', {
url: '/account',
templateUrl: 'main/account/account.tpl.html', // this is not very modular
controller: 'AccountCtrl',
});
})
This is not very modular, and could become difficult to maintain in large projects. I would need to remember to change the templateUrl path every time I moved any of these modules. It would be nice if there was some way to load the template relative to the current file like:
templateUrl: './account.tpl.html'
Is there any way to do something like this in angular?
The best way to do this now is using a module loader like browserify, webpack, or typescript. There are plenty of others as well. Since requires can be made from the relative location of the file, and the added bonus of being able to import templates via transforms or loaders like partialify, you don't even have to use template urls anymore. Just simply inline the Template via a require.
My old answered is still available below:
I wrote a post on exactly this subject and spoke on it at our local Angular Meetup. Most of us are now using it in production.
It is quite simple as long as your file structure is represented effectively in your modules. Here is a quick preview of the solution. Full article link follows.
var module = angular.module('myApp.things', []);
var all = angular.module('myApp.things.all', [
'myApp.things',
'things.someThing',
'things.someOtherThing',
'things.someOtherOtherThing',
]);
module.paths = {
root: '/path/to/this/thing/',
partials: '/path/to/this/thing/partials/',
sub: '/path/to/this/thing/sub/',
};
module.constant('THINGS_ROOT', module.paths.root);
module.constant('THINGS_PARTIALS', module.paths.partials);
module.constant('THINGS_SUB', module.paths.sub);
module.config(function(stateHelperProvider, THINGS_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things',
url: '/things',
templateUrl: THINGS_PARTIALS + 'things.html',
});
});
And then any sub modules or "relative" modules look like this:
var module = angular.module('things.someThing', ['myApp.things']);
var parent = angular.module('myApp.things');
module.paths = {
root: parent.paths.sub + '/someThing/',
sub: parent.paths.sub + '/someThing/sub/',
partials: parent.paths.sub + '/someThing/module/partials/',
};
module.constant('SOMETHING_ROOT', module.paths.root);
module.constant('SOMETHING_PARTIALS', module.paths.partials);
module.constant('SOMETHING_SUB', module.paths.sub);
module.config(function(stateHelperProvider, SOMETHING_PARTIALS) {
stateHelperProvider.setNestedState({
name: 'things.someThing',
url: "/someThing",
templateUrl: SOMETHING_PARTIALS + 'someThing.html',
});
});
Hope this helps!
Full Article: Relative AngularJS Modules
Cheers!
I think you'll eventually find that maintaining the paths relative to the js file will be harder, if even possible. When it comes time to ship, you are most likely going to want to concatenate all of your javascript files to one file, in which case you are going to want the templates to be relative to the baseUrl. Also, if you are fetching the templates via ajax, which Angular does by default unless you pre-package them in the $templateCache, you are definitely going to want them relative to the baseUrl, so the server knows where to find them once your js file has already been sent to the browser.
Perhaps the reason that you don't like having them relative to the baseUrl in development is because you aren't running a server locally? If that's the case, I would change that. It will make your life much easier, especially if you are going to work with routes. I would check out Grunt, it has a very simple server that you can run locally to mimic a production setup called Grunt Connect. You could also checkout a project like Yeoman, which provides a pre-packaged front end development environment using Grunt, so you don't have to spend a lot of time getting setup. The Angular Seed project is a good example of a Grunt setup for local development as well.
I've been chewing on this issue for a while now. I use gulp to package up my templates for production, but I was struggling to find a solution that I was happy with for development.
This is where I ended up. The snippet below allows any url to be rewired as you see fit.
angular.module('rm').config(function($httpProvider) {
//this map can be defined however you like
//one option is to loop through script tags and create it automatically
var templateMap = {
"relative.tpl.html":"/full/path/to/relative.tpl.html",
etc...
};
//register an http interceptor to transform your template urls
$httpProvider.interceptors.push(function () {
return {
'request': function (config) {
var url = config.url;
config.url = templateMap[url] || url;
return config;
}
};
});
});
Currenly it is possible to do what you want using systemJs modules loader.
import usersTemplate from './users.tpl';
var directive = {
templateUrl: usersTemplate.name
}
You can check good example here https://github.com/swimlane-contrib/angular1-systemjs-seed
I had been using templateUrl: './templateFile.tpl.html but updated something and it broke. So I threw this in there.
I've been using this in my Gruntfile.js html2js object:
html2js: {
/**
* These are the templates from `src/app`.
*/
app: {
options: {
base: '<%= conf.app %>',
rename: function( templateName ) {
return templateName.substr( templateName.lastIndexOf('/') );
}
},
src: [ '<%= conf.app %>/**/{,*/}<%= conf.templatePattern %>' ],
dest: '.tmp/templates/templates-app.js'
}
}
I know this can lead to conflicts, but that is a smaller problem to me than having to edit /path/to/modules/widgets/wigdetName/widgetTemplate.tpl.html in every file, every time, I include it in another project.

Resources