AngularJS load config on app start - angularjs

I need to load a config file (JSON format) upon my AngularJS app startup in order to load few parameters which will be used in all api calls. So I was wondering if it is possible to do so in AngularJS and if yes where / when I shall be loading the config file?
Note:
- I will need to save the config file parameters in a service, so I will need to load the json file content before any controller is loaded but with service units available
- Using an external json file is a must in my case here as the app client need to be able to update the app configuration easily from external file without the need to go through the app sources.

EDITED
It sounds like what you are trying to do is configure a service with parameters. In order to load the external config file asynchronously, you will have to bootstrap the angular application yourself inside of a data load complete callback instead of using the automatic boostrapping.
Consider this example for a service definition that does not actually have the service URL defined (this would be something like contact-service.js):
angular.module('myApp').provider('contactsService', function () {
var options = {
svcUrl: null,
apiKey: null,
};
this.config = function (opt) {
angular.extend(options, opt);
};
this.$get = ['$http', function ($http) {
if(!options.svcUrl || !options.apiKey) {
throw new Error('Service URL and API Key must be configured.');
}
function onContactsLoadComplete(data) {
svc.contacts = data.contacts;
svc.isAdmin = data.isAdmin || false;
}
var svc = {
isAdmin: false,
contacts: null,
loadData: function () {
return $http.get(options.svcUrl).success(onContactsLoadComplete);
}
};
return svc;
}];
});
Then, on document ready, you would make a call to load your config file (in this case, using jQuery). In the callback, you would then do your angular app .config using the loaded json data. After running the .config, you would then manually bootstrap the application. Very Important: do not use the ng-app directive if you are using this method or angular will bootstrap itself See this url for more details:
http://docs.angularjs.org/guide/bootstrap
Like so:
angular.element(document).ready(function () {
$.get('/js/config/myconfig.json', function (data) {
angular.module('myApp').config(['contactsServiceProvider', function (contactsServiceProvider) {
contactsServiceProvider.config({
svcUrl: data.svcUrl,
apiKey: data.apiKey
});
}]);
angular.bootstrap(document, ['myApp']);
});
});
UPDATE: Here is a JSFiddle example: http://jsfiddle.net/e8tEX/

I couldn't get the approach suggested my Keith Morris to work.
So I created a config.js file and included it in index.html before all the angular files
config.js
var configData = {
url:"http://api.mydomain-staging.com",
foo:"bar"
}
index.html
...
<script type="text/javascript" src="config.js"></script>
<!-- compiled JavaScript --><% scripts.forEach( function ( file ) { %>
<script type="text/javascript" src="<%= file %>"></script><% }); %>
then in my run function I set the config variables to $rootScope
.run( function run($rootScope) {
$rootScope.url = configData.url;
$rootScope.foo = configData.foo;
...
})

You can use constants for things like this:
angular.module('myApp', [])
// constants work
//.constant('API_BASE', 'http://localhost:3000/')
.constant('API_BASE', 'http://myapp.production.com/')
//or you can use services
.service('urls',function(productName){ this.apiUrl = API_BASE;})
//Controller calling
.controller('MainController',function($scope,urls, API_BASE) {
$scope.api_base = urls.apiUrl; // or API_BASE
});
//in html page call it
{{api_base}}
There are also several other options including .value and .config but they all have their limitations. .config is great if you need to reach the provider of a service to do some initial configuration. .value is like constant except you can use different types of values.
https://stackoverflow.com/a/13015756/580487

Solved by using constant.
Like providers you can configure it in .config phase.
Everything else like Keith Morris wrote before.
So the actual code would be look like this:
(function () {
var appConfig = {
};
angular.module('myApp').constant('appConfig', appConfig);
})();
then in app.bootstrap.js
(function () {
angular.element(document).ready(function () {
function handleBootstrapError(errMsg, e) {
console.error("bootstrapping error: " + errMsg, e);
}
$.getJSON('./config.json', function (dataApp) {
angular.module('myApp').config(function (appConfig) {
$.extend(true, appConfig, dataApp);
console.log(appConfig);
});
angular.bootstrap(document, ['myApp']);
}).fail(function (e) {
handleBootstrapError("fail to load config.json", e);
});
});
})();

To json config file, there is a practice example on Jaco Pretorius blog's. Basically:
angular.module('plunker', []);
angular.module('plunker').provider('configuration', function() {
let configurationData;
this.initialize = (data) => {
configurationData = data;
};
this.$get = () => {
return configurationData;
};
});
angular.module('plunker').controller('MainCtrl', ($scope, configuration) => {
$scope.externalServiceEnabled = configuration.external_service_enabled;
$scope.externalServiceApiKey = configuration.external_service_api_key;
});
angular.element(document).ready(() => {
$.get('server_configuration.json', (response) => {
angular.module('plunker').config((configurationProvider) => {
configurationProvider.initialize(response);
});
angular.bootstrap(document, ['plunker']);
});
});
Plunker: http://plnkr.co/edit/9QB6BqPkxprznIS1OMdd?p=preview
Ref: https://jacopretorius.net/2016/09/loading-configuration-data-on-startup-with-angular.html, last access on 13/03/2018

Related

AngularJS - Best way to provide HTTP URLs at run time

I have an AngularJS application, which is supposed to call several Spring-based REST API's, url of which would be dynamic based on the environment.
Like if I am running on dev environment, REST URL would be localhost:7777/..
If on production, it would be productionserver:7777/....
Now I have to provide these API URLs to Angular.
What is the best way to configure these REST URLs at run time in my AngularJS application?
You could place all of your api configuration into a single json file named for example config.json, and then before application is bootstrapped you can fetch that data and make a constant out of it and after inject it anywhere you need, something like the following:
Lets say config.json contains these values:
{
"ApiUrls": {
"ApiUrl1": "value1",
"ApiUrl2": "value2",
"ApiUrl3": "value3"
}
}
Injecting configuration api urls before bootstrapping:
(function () {
fetchData().then(bootstrapApp);
function fetchData() {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
return $http.get("config.json").then(function (response) {
app.constant("ApiConfiguration", response.data.ApiUrls);
}, function (errorResponse) {
alert(errorResponse);
// Handle error case
});
}
function bootstrapApp() {
angular.element(document).ready(function () {
angular.bootstrap(document, ["app"]);
});
}
}());
and for example your service:
app.factory('factory', factory);
factory.$inject = ['ApiConfiguration'];
function factory(ApiConfiguration){
var serviceFactory = {
ApiUrl1: ApiConfiguration.ApiUrl1
}
return serviceFactory;
}
This way you can set your values inside the config.json file as per enviroment. Locally it will be localhost:7777/.., in production it will be productionserver:7777/... etc..
Create Service for rest urls or REST Data:-
(function () {
var injectParams = ['$location'];
var service = function ($location) {
var vm = this;
vm.baseUrl = 'http://' + $location.host() +':'+$location.port() + '/api/v1/';
return vm;
};
//bootstrapping
service.$inject = injectParams;
angular.module('showdown').service('restURL', service);
}());
Depending on your setup, you may want to inject your URLs via ng-init. Just make sure to JS and HTML escape the string, if necessary.
So your HTML would look something like this:
<div ng-app="myApp" ng-init="
myUrl='${myUrl?html?js_string}';
myUrl2='${myUrl2?html?js_string}';">
And your Javascript would look something like this:
angular.controller("myApp", () => ($scope) => {
$scope.myUrl; //url is here
});
Create a const in ur app like SERVER-ENV and write a build task to change the value of this constant based on the server environment.
Now you can create URL using this const e.g. SERVER-ENV + "";
This task can be automated with gulp or grunt.

AngularJs minification process

I'm finishing a web app and all works like a charm.
I use Grunt to join all my .js files in one unique file and that's the one I use in the index.html file to load the code.
The issue is when I use the .min. version of the file generated by grunt using 'grunt-contrib-uglify' task.
When I reload the page, the following error arises:
angular.js:38Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.5.5/$injector/modulerr?p0=myapp&p1=Error%…ogleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.5.5%2Fangular.min.js%3A39%3A222)
I've been reading around this on Google but no success.
How can I solve this?
EDIT:
This is a typical file controller (all have the same structure):
(function() {
var app = angular.module("post", []);
var controllers = {};
controllers.postCtrl = ['$scope', '$rootScope', 'myFactory', function($scope, $rootScope, myFactory) {
$scope.loading = {state:false};
$scope.filters = $scope.filters || myFactory.authors;
$scope.init = function() {
var idx = myFactory.get_author_by_index(Number($('input[name="author"]').val()));
$scope.filterSelected = $scope.filters[idx];
angular.element(document).ready(function () {
angular.forEach($('div.general_page_content').find('a'), function(value, index) {
$(value).attr('target', "_new");
})
angular.forEach($('div.general_page_content').find('iframe'), function(value, index) {
$(value).attr("width", "100%");
})
angular.forEach($('div.general_page_content').find('img'), function(value, index) {
$(value).attr("width", "100%").css('width', '100%');
})
myFactory.containerResize();
});
}
$rootScope.$on('loading', function(evt, value) {
$scope.loading.state = value;
});
$rootScope.$on('autocomplete:focus', function(ev) {
$scope.search_focus = true;
});
$rootScope.$on('autocomplete:blur', function(ev) {
$scope.search_focus = false;
});
$scope.showSocialShare = function(ev) {
$scope.url = decodeURIComponent($('input[name="url"]').val());
$scope.text = decodeURIComponent($('input[name="text"]').val());
$scope.img = decodeURIComponent($('input[name="img"]').val());
myFactory.showSocialShare($scope, ev);
};
$scope.favorite_post = function(ev, id, title) {
myFactory.favorite_post($scope, ev, id, title);
}
$scope.fetchPostsChange = function(selected) {
document.location = '/blog/?author='+selected.id;
}
$scope.search = function(text) {
document.location = '/blog/?search='+encodeURIComponent(text);
}
$scope.go_to_favorites_post = function() {
document.location = '/blog/archive/';
}
$scope.init();
}];
app.controller(controllers);
})();
UPDATE:
I took only two .js files and process them to minified it and check if the same error arises or not. The curious thing is that taking into account only two files, the same error arises, so I paste the minified file for you to be able to detect what's wrong.
!function(){angular.module("myapp",["ngMaterial","ngMessages","ngStorage","toaster","ngMdIcons","lvl.services","smart-table","angularGrid","ngFileUpload","angular-timeline","header","dashboard","sidebar","autocomplete","timeline","sidebarCollection","myappFactory","objectCtrl","homeCtrl","Collections","Collection","posts","post","model","postArchive","720kb.socialshare","services","footer"]).config(function(a,b,c){a.theme("default").primaryPalette("lime").accentPalette("grey").warnPalette("red"),a.theme("darkTheme").primaryPalette("lime").accentPalette("grey").warnPalette("red").dark(),b.enabled(!1),c.get("user")})}(),function(){var a=angular.module("autocomplete",[]),b={},c={};b.autocompleteCtrl=["$http","$scope","$mdBottomSheet","$sessionStorage","myappFactory",function(a,b,c,d,e){b.init=function(){b.session=d,angular.isUndefined(b.session.advance_search)&&(b.session.advance_search={select_all:!0,show_cost:!0,show_free:!0,items:[{name:"Thingiverse",selected:!0},{name:"Youmagine",selected:!0},{name:"MyMinifactory",selected:!0},{name:"Cults 3D",selected:!0},{name:"Pinshape",selected:!0},{name:"Turbosquid",selected:!0},{name:"Shapeways",selected:!0},{name:"GrabCAD",selected:!0},{name:"CGTrader",selected:!0},{name:"Threeding",selected:!0}]})},b.querySearch=function(c){var d=c.trim();return d&&d.length>2?(b.isFetching=!0,a.get(e._myapp_link+"/api/index.php/myapp/autocomplete/"+encodeURIComponent(d)).then(function(a){return a.data})):void 0},b.collectionSearch=function(c){var d=c.trim();return d&&d.length>2?(b.isFetching=!0,a.get(e._myapp_link+"/api/index.php/myapp/collection_search/"+encodeURIComponent(d)).then(function(a){return a.data})):void 0},b.search=function(a){var c="";b.session.advance_search.show_cost&&!b.session.advance_search.show_free?c+=" free:0 ":!b.session.advance_search.show_cost&&b.session.advance_search.show_free&&(c+=" free:1 "),angular.forEach(b.session.advance_search.items,function(a,d){(b.session.advance_search.select_all||a.selected)&&(c+=" plataforma:"+a.name)}),window.location="/?search="+encodeURIComponent(a)+"&params="+Base64.encode(c)},b.go_to_collection=function(a){window.location="/collections/"+encodeURIComponent(a)},b.showAdvancedSearch=function(){c.show({templateUrl:"/advanced_search_sheet.html",controller:"ListBottomSheetCtrl"})},b.init()}],b.ListBottomSheetCtrl=["$scope","$mdBottomSheet","$sessionStorage","myappFactory",function(a,b,c,d){a.session=c,a.toggle_all_sites=function(){a.session.advance_search.select_all=!a.session.advance_search.select_all,a.session.advance_search.select_all&&angular.forEach(a.session.advance_search.items,function(a,b){a.selected=!0})},a.toggle_advance_search=function(b){if(a.session.advance_search.select_all)a.session.advance_search.items[b].selected=!0,d.showMessage({msg:"Uncheck 'All repositories' first!"});else if(a.session.advance_search.items[b].selected=!a.session.advance_search.items[b].selected,!a.session.advance_search.items[b].selected){var c=0;angular.forEach(a.session.advance_search.items,function(a,b){a.selected&&++c}),c||(a.session.advance_search.items[b].selected=!0,d.showMessage({msg:"There must be at least 1 respository selected!"}))}},a.free_cost_checked=function(b){var c="cost"==b?!a.session.advance_search.show_cost:!a.session.advance_search.show_free;c?"cost"==b?a.session.advance_search.show_cost=!0:a.session.advance_search.show_free=!0:"cost"==b?a.session.advance_search.show_free?a.session.advance_search.show_cost=!1:d.showMessage({msg:"Free and Price cannot be unchecked!"}):"free"==b&&(a.session.advance_search.show_cost?a.session.advance_search.show_free=!1:d.showMessage({msg:"Free and Price cannot be unchecked!"}))}}],c.myEnter=function(){return function(a,b,c){b.bind("keydown keypress",function(b){13===b.which&&(a.$apply(function(){a.search(a.searchText)}),b.preventDefault())})}},c.onBlur=["$rootScope","$mdUtil","$timeout",function(a,b,c){return{require:"^mdAutocomplete",link:function(d,e,f,g){c(function(){var c=(e.find("input"),e[0],g.blur),h=g.focus;g.blur=function(){c.call(g),b.nextTick(function(){a.$broadcast("autocomplete:blur"),d.$eval(f.mdBlur,{$mdAutocomplete:g})})},g.focus=function(){h.call(g),b.nextTick(function(){a.$broadcast("autocomplete:focus"),d.$eval(f.mdFocus,{$mdAutocomplete:g})})}})}}}],a.controller(b).directive(c)}();
As you certainly found on Google, that error depends on Variable Mangling https://github.com/gruntjs/grunt-contrib-uglify#mangle.
So, when you mangle $rootScope becomes a and, of course, angular dependency injection cannot resolve it:
angular
.module('test', [])
.run(function($injector) {
console.log(
"$rootScope exists?",
$injector.has('$rootScope')
);
try {
// mangle $rootscope => a
$injector.get('a');
} catch(e) {
console.log('a exists?', e.message);
}
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<section ng-app="test"></section>
There are many ways to manage this issue:
Disable Variable Mangling (not a good way, but is a solution).
Always use Angular DI Annotation
a. Array notation: someModule.run(["$rootScope", function(a) {}]);
b. $inject Property: runFn.$inject = ['$rootScope']; function runFn(a) {}; someModule.run(runFn);
Use ngAnnotate that does annotation at build time. I suggest you this option because you don't need to take care about annotation...
Important thing:
Always run your code in angular strict di mode, that gives you the opportunity of control each annotation issue.
If you minify, each module's injections must be named. So before you may have had:
app.factory('myFactory', function($route) {
});
Now you must name/declare them as well otherwise angular doens't know what to inject:
app.factory('myFactory', ['$route', function($route) {
}]);
Why?
Minification turns everything to small variables amongst other things. So our factory becomes:
app.factory('myFactory', function(a) {
a.<functioncall>
});
Angular doens't know what "a" is and so must be told:
app.factory('myFactory', ['$route', function(a) {
a.<functioncall>
}]);
And so it now knows and a= $route and so on.

AngularJS: Module not found error when creating module from asynchronous function's callback

When i try to create angularjs module in usual way, it works perfect, but when i try to execute same code inside a callback function of aync function call, it throws error that module not found:
The following code works fine:
var myApp = angular.module('SSApp',[]);
myApp.controller('config', function($scope) {
});
But following throws error:
Init_Data(function() {
initApp();
});
function initApp() {
var myApp = angular.module('SSApp',[]);
myApp.controller('config', function($scope) {
});
}
function Init_Data(callback) {
chrome.storage.local.get(null, function(data) {
window.data = data;
callback();
});
}
I've defined ng-app="SSApp" directive in respective html code.
The reason your code is not doing what you expect is because, Angular tries to bootstrap the module "SSApp" automatically when the DOM is ready. But, finds no such module defined by your JavaScript code when it tries to do so.
You probably have ng-app="SSApp" somewhere in your HTML which is why Angular tries to bootstrap the module automatically.
You can choose to bootstrap the module manually by removing the ng-app directive and doing
angular.bootstrap(document.documentElement, ['SSApp']);
This is the change you have to do:
var myApp = angular.module("SSApp", []);
Init_Data(function () {
initApp();
});
function initApp() {
myApp.controller('config', function ($scope) {
});
console.log(myApp)
}
function Init_Data(callback) {
setTimeout(function () {
callback();
},4000);
}
From what I understand in your code, first you want to load data and then to add config controller to your app...so define your app first and then in your callback configure

AngularJs - RequireJs - Socket.io : Not playing well together

I have a 2 part issue:
I. --- When I load socket.io in my requirejs path file, while it may appear in browser's scripts collection, the moment I use it in a factory object, I receive the error, "io" is not defined. I do not get the error however, if I load socket.io.js from the HTML page directly. I found another person had the exact issue here but with no resolution.
II. --- I am using ocLazyLoad, and my app is modularized per function. I opted to place the $socket factory object I copied from Brian Ford's example directly in my root module ['app']. I know services in a lazyload scenario need to be loaded every time they are used, regardless of prior calls to them. But, this seems wrong. If I wish to make my app real time, that means I have to list it as a dependency in every state. There should be a more efficient way to do this but I cannot see how.
Issue I:
Establishing RequireJs dependency and adding to bootstrap
a. RequireJs's path file:
//socket.io
'socket.io' : '/vendor/socket.io-client/lib/socket.io'
//shim
'socketio': {exports: 'io'}
b. Bootstrap file
window.name = "NG_DEFER_BOOTSTRAP!";
define([
'require',
'jquery',
'socket.io',
'angular',
.....
Observation: This is all working as expected, as evidenced by socket.io.js now in my browser's scripts collection on page load.
$socket factory service
a. app.socket.js (factory file from Brian Ford) file:
define(['app'], function (app) {
app.factory('$socket',['$rootScope', function ($rootScope) {
/////////////////////
// PROBLEM IS HERE //
/////////////////////
var socket = io.connect();
return {
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
}
});
})
}
};
}]); });
b. Controller using $socket factory
define(['angular'], function(ng) {
'use strict';
/* #ngInject */
MyDashboardCtrl.$inject = ['$socket','MyDashboardSvc'];
ng.module('MyDashboard').controller('MyDashboardCtrl', MyDashboardCtrl);
function MyDashboardCtrl($socket, MyDashboardSvc) {
....
Obervation: With socket.io.js being loaded from require's path file, once the controller calls the factory as a dependency, I receive this error:
ReferenceError: io is not defined
If instead, I load socket.io.js straight from the HTML, the problem goes away:
<script src="vendor/socket.io-client/lib/socket.io.js"></script>
<script src="app/require/rconfig.js"></script>
<script src="vendor/requirejs/require.js" data-main="app/app.bootstrap.js"></script>
Issue II:
I would like to make my entire app real time. Does that mean, if I intend to utilize lazy loading, I will need to list the $socket factory in every BLESSED $state?
I hate my solution. I put the socket.io object on the windows inside of requirejs, and then find it there using the $window object in angular. It really is atrocious - but it will allow me to continue to code while I find a better solution. Here lie the relevant and irrelevant bits:
<script text="text/javascript">
require.config({
paths: {
angular: "<path to angular>"
socketIO: [
"/socket.io/socket.io"
],
myNgApplicationJs: "/client/my-angular-all"
},
shim: {
socketIO: { deps: [] },
angular: { deps: [
"jQuery",
"socketIO"
] },
myNgApplicationJs: {deps: [
"angular", "socketIO"
]}
}
});
require([
"myNgApplicationJs"
,"socketIO"
], function(noop, io) {
// a hack to make the socket.io available on the $window in angular
window.io = io
angular.bootstrap(document, ['myNgApplication']);
return {};
});
</script>
Then (based off of code poached from Brian Ford):
angular.module('myNgApplication').factory('MySocketIO', function ($log, $window, $rootScope) {
if(!$window.io) {
throw new Exception("assure the socket.io is added to the window object. e,g, window.io")
}
// Hack socket.io off of the $window object
var io = $window.io
var socket = io();
return {
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
}
});
})
}
};
});
This allows me to abstract away the hack - the best I can. Should I find a better solution I only need to change requirejs peice and the MySocketIO implementation.
I then code up business level factories like this:
angular.module('myNgApplication').factory('BlahProxy', function ($log, MyHttp, MySocketIO) {
var Blah = {}
Blah.onUpdate = function(callback) {
// don't expose socket-io message type to the controllers
MySocketIO.on('send:testIV', callback)
}
return Blah
})
Then leverage those factories in my controllers:
angular.module('myNgApplication').controller('myBlahGridCtrl', function ($scope, $log, BlahProxy) {
$scope.messages = []
// handle whatever message types explicitly
BlahProxy.onUpdate(function (message) {
$scope.messages.push(message);
})
})
I was facing the same problem, It solved by adding 'socket.io' in my socketfactory file dependencies. In your case, try the following change:
a. app.socket.js file:
define(['app', 'socket.io'], function (app, io) {
app.factory('$socket',['$rootScope', function ($rootScope) {
//........

Inject a config in AngularJS

I have a config that I'd like to be able to inject into Angular services, directives, etc. What is the best way to go about doing this?
I was playing with the idea of making config module:
'use strict';
angular.module('config', [])
But I wasn't sure how to construct the object literal that would be the actual config object that gets injected:
angular.module('myModule', ['ngResource', 'config'])
.factory('myservice', function ($resource) {
return $resource(config.myservice,{}, {
// whatever
})
});
Would it be okay to just expose config as a service and inject that?
I would say, the strategy varies, depending on what sort of config you have, but you have several options:
Module wide constants
If you only need several constants, you can use .value(), like this:
var app;
app = angular.module("my.angular.module", []);
app.value("baseUrl", "http://myrestservice.com/api/v1");
//injecting the value
app.controller("MyCtrl", ['baseUrl', function (baseUrl) {
console.log(baseUrl); // => "http://myrestservice.com/api/v1"
}]);
See a more detailed answer here.
Fetching the config/config service
What i personally like to do is fetch my configuration from somewhere else via a service just as normal. It doesn't matter, if this is a remote location or just static information.
var app;
app = angular.module("my.angular.config", []);
app.service('Configuration', [function() {
return {
getbaseUrl: function() { return "http://myrestservice.com/api/v1" },
getConfig: function() {
return {
answer: 42,
question: "??"
}
}
}
}]):
EDIT: example with an external fetch:
var app;
app = angular.module('my.module.config', []);
app.factory('ConfigurationData', ['$http', '$q', function(http, q) {
var deferredConfig = q.defer();
//error handling ommited
http.get('http://remote.local/config.json').success(function(data) {
return deferredConfig.resolve(data);
});
return {
getConfig: function() {
return deferredConfig.promise;
}
};
}]);
With this service, you can inject your config into other services, however, you can run into timing issues, as you have to inject and resolve the promise given by the service before anything you want to do with the config:
var app;
app = angular.module("my.other.module", ['my.module.config']);
app.factory('MyAwesomeService', ['ConfigurationData', function(config) {
config.getConfig().then(function(config) {
//do something with your config.
});
}]);
You get a bit more fine control here, as you can react to different inputs. Again, it depends on your use case. You can use a factory here if you need additional logic for structuring the configuration.
Lastly, if you want even more control over configuration, you can make a
Custom Provider
Providers can be pretty useful, but i think they are a bit harder to design. Considering a baseUrl from the configuration needed for your app to work, you could write a provider for the service needing the value of the baseUrl like this:
var app;
app = angular.module('my.angular.module', []);
app.provider("RestServiceProvider", function(){
this.baseUrl = 'http://restservice.com';
this.$get = function() {
var baseUrl = this.baseUrl;
return {
getBaseUrl: function() { return this.baseUrl; }
}
};
this.setBaseUrl = function(url) {
this.baseUrl = url;
};
});
This lets you do cool stuff in the config phase of your application:
app.config(['RestserviceProvider', function(restServiceProvider) {
restServiceProvider.setBaseUrl('http://wwww.myotherrestservice.com');
}]);
Every instance you fetch in a service/controller/etc. of RestService will now have the baseUrl set from the config phase from the moment you inject it.
For a more detailed overview, i suggest this gist.
Creating a stand-alone module that only has a service or a directive (or both) is a great way to make application-independent angular code. If you do this you can easily just take that .js file and plop it into any project, and all you need to do is inject it into your applications and it just works.
So doing something like:
angular.module('config', [])
.factory('config', function() {
return {
theme : 'nighttime',
cursor : 'sword',
...
};
});
Then you can just inject it into any application like so:
angular.module('myModule', ['config'])
.factory('myservice', function (config) {
var theme = config.theme;
var cursor = config.cursor;
// do stuff with night-time swords!
});
This is actually how the angular-ui team package all their directives, each directive is contained within its own module, which makes it easy for others to just take that code and reuse it in all their apps.

Resources