Provide global configuration in AngularJS - angularjs

currently i provide my application with a constant called "config" to inject some specific application-wide configuration like the API endpoint URL and so on.
This works like expected for my main module "app" in the configuration section.
app.config(['config', function(config) {
console.log(config.api); // http://api.endpoint.com
}];
The problem is, that i have a separate module like "app.auth" where i wish you configure it with some values from this global constant, but there i get an error, that the config is unknown.
How i can solve this? Inject a service in the configuration area isn't a solution because AngularJS don't provide the services on the configuration block.
Thank you in advance.

First off you answer your question about why 'config' is not available to your app.
Is your app set as a dependency of 'app.auth'?
var app = angular.module('myApp', ['ngResource']);
var authApp = angular.module('myApp.auth', ['myApp']);
Secondly using a constant to set multiple values is a poor use of constant, that would be a great job for a provider. Here is an example:
module.provider('GlobalConfig', function ($resourceProvider) {
var valOne = 'Some Default';
this.setOne = function(value) {
valOne = value;
};
var valTwo = 'Another Default';
this.setTwo = function(value) {
valTwo = value;
};
this.$get = function($resource) {
return {
getOne: function() {
return valOne;
},
getTwo: function() {
return valTwo;
}
};
};
});
From a .config the provider above can be injected by using 'GlobalConfigProvider' and only the setters would be available. Then for directives and services 'GlobalConfig' can be injected and only the getters would be available.

Related

AngularJS - how to use factory inside a provider

I wonder how can i use factory inside a provider for using it inside config.
Since i understand config can be injected only with providers - i wonder how can i achieve the following functionality
app.provider('getUserLanguageProvider',['$injector', function($injector) {
this.$get = function(getUserLang) { // calling a factory
var userLang = getUserLang.getLang()
return {
getLang: function() {
return userLang
}
}
};
}]);
app.config(['$translateProvider', 'getUserLanguageProvider', function ($translateProvider, getUserLanguageProvider) {
 
const lang = getUserLanguageProvider.getLang() // get the language key from provider
$translateProvider.preferredLanguage(lang); // set the language key brought by getUserLang.getLang() factory
}]);
I've tried many versions of what i've described - but none works.
Don't append Provider to the provider name. Simply use the name of the service:
̶a̶p̶p̶.̶p̶r̶o̶v̶i̶d̶e̶r̶(̶'̶g̶e̶t̶U̶s̶e̶r̶L̶a̶n̶g̶u̶a̶g̶e̶P̶r̶o̶v̶i̶d̶e̶r̶'̶,̶[̶'̶$̶i̶n̶j̶e̶c̶t̶o̶r̶'̶,̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶(̶$̶i̶n̶j̶e̶c̶t̶o̶r̶)̶ ̶{̶
app.provider('getUserLanguage',['$injector', function($injector) {
this.$get = ['$http', function($http) { // calling a factory
var userLang = getUserLang.getLang()
return {
getLang: function() {
// ....
}
}
}];
}]);
The $injector service will automatically append the Provider suffix to the configuration object. The configuration object will then be injectable into .config functions as the name of the service appended with Provider as a suffix.

Why anglar.mock.module does not $provide values like it $provides constants

I've looked at the documentation for angular.mock.module and a couple of examples of others using it but I seem to be running into an issue in my use-case that I don't understand.
I'm running Jasmine (2.4.1) tests with angular (1.4.9) and I have my angular app separated into multiple modules. When I attempt to mock out certain parts of my app for unit testing I want to mock out entire modules (or providers) so that I only expose the pieces I use.
Here is a very simple app that has a main module plunker which depends on plunker.service. plunker.service depends on plunker.constant.
var app = angular.module('plunker', ['plunker.service']);
app.controller('MainCtrl', function($scope, valueService, appService) {
$scope.init = function() {
$scope.appValue = valueService.getValue();
$scope.appIsRunning = appService.getStatus();
};
});
angular.module('plunker.service', ['plunker.constant'])
.service('appService', function(appSettings) {
var vm = this;
vm.getStatus = function () {
if (appSettings.isRunning) {
return true;
} else {
return false;
}
};
})
.service('valueService', function(valueSettings) {
var vm = this;
vm.getValue = function () {
return valueSettings.value;
}
});
angular.module('plunker.constant', [])
.constant('appSettings', { isRunning: true })
.constant('valueSettings', { value: 10 });
In my Jasmine tests I have a beforeEach() that registers my modules using module (aka angular.mock.module).
I have seen 3 ways of using module
string
function with $provide
object
You can see below that I use the module('plunker') (string) to register my main module and I have 3 ways of mocking out my appSettings constant (A, B, C). You will notice that the function with $provide.constant works fine but function with $provide.value does not and object does not.
beforeEach(function() {
module('plunker');
function useFunction(typeofProvider) {
module(function($provide) {
$provide[typeofProvider]('appSettings', { isRunning: false });
});
}
function useObject() {
module({
appSettings: { isRunning: false }
});
}
// A. THIS WORKS! //
useFunction('constant');
// B. THIS DOES NOT //
// useFunction('value');
// C. THIS ALSO DOES NOT!! //
// useObject();
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope
});
});
});
I have also seen people use the following syntax...
beforeEach(function() {
var mockService = function () {
var mockValue = 10;
this.value = mockValue;
};
// D.
module('a.module.name', function newProviders($provide){
$provide.service('realService', mockService);
});
});
My questions
In my test code, why does A. work but B. and C. do not?
Is D. equivalent to calling module('a.module.name'); followed by module(function newProviders($provide) { ... });? Does placing both in the same module() call have any special effects on how things are registered or is it just a shorthand? (based on the documentation it should be a shorthand)
Related to Jasmine, specifically, do all beforeEach() calls run in the same top-to-bottom order with every execution?
Here is my plunker for the above app and jasmine code
Thanks
This happens because of how Angular injector works. In fact, there are two different injectors in Angular. The one (available as $injector in config blocks) deals with service providers. Another one (available as $injector anywhere else) deals with service instances. Providers and instances are cached and stored internally.
$provide.constant('service') creates both provider and instance of name 'service' at call time.
All other types of services are lazily instantiated. They create 'serviceProvider' provider at call time, but 'service' instance is created on the first injection.
Since Angular service instance is a singleton, it refers to instance cache before the instantiation. If the instance is in the cache, it is reused and not instantiated. constant service instance is eagerly instantiated, so only another constant can override the instance.
Object properties in angular.mock.module are shortcuts for $provide.value, and useObject() equals to useFunction('value') in this example.
As long as module order stays the same,
module('a.module.name', function ($provide) { ... });
is indeed a shortcut for
module('a.module.name');
module(function ($provide) { ... });
Due to the fact that appSettings object isn't used in config blocks (the primary use of constant service), it is more convenient to make it value.

is it possible in angular to set the debug log level at runtime?

Is it possible to switch the $logProvider.debugEnabled([flag]); at runtime?
The current situation:
The angular client load settings from the server at the run phase. Depends on the settings I would like to set the method $logProvider.debugEnabled([flag]).
Thanks in advance, Stevo
The short answer for this is: no, not really.
Once your application hass been configured through your .config() block, no further configuration can take place after the application has bootstrapped.
This is due to the way providers work; they're only available at configuration time. There might be a way to force the configuration, and then manually re-inject the new $log service into all of your controllers, but if there is a way to do that, I'm not sure how.
I've decorated $log.debug(...) to change the loglevel at runtime.
Looking at Enhancing AngularJS Logging using Decorators, I got the idea for the following code snippet:
(function () {
var KEY = "debugEnabled";
angular.module("service.config", [])
.config(function ($provide, $logProvider) {
// AngularJS has debug enabled by default, but just to be sure...
$logProvider.debugEnabled(true);
// Disabling localStorageDebug (if not set)
if (localStorage.getItem(KEY) === null) {
localStorage.setItem(KEY, "false");
}
// add a check for localStorageDebug before actually calling $log.debug(...)
$provide.decorator('$log', function ($delegate) {
var debugFunction = $delegate.debug;
$delegate.debug = function () {
if (localStorage.getItem(KEY) !== "false") {
debugFunction.apply(undefined, arguments)
}
};
return $delegate;
});
})
.service("ConfigService", function ($log) {
this.debugEnabled = function (flag) {
$log.info("Setting debugEnabled to " + flag);
localStorage.setItem(KEY, flag.toString());
}
});
})();
// exposing ConfigService to global scope (be aware of possible clashes!),
// therefore making it easily accessible from the console
var cfg;
window.onload = function () {
cfg = angular.element(document.body).injector().get("ConfigService");
};
The decorator only forwards calls to $log.debug if debugEnabled is set to true in your local storage - the value can be changed through the ConfigService service.
Now you can just call ConfigService#debugEnabled with the value you've loaded from your server to change the loglevel.
Thanks to the last four lines, you can also simply call cfg.debugEnabled(true) on your console to enable debug mode at runtime.
If you don't like to type into the console, you could avoid the global cfg and use javascript bookmarks (or elements on your website) to change the log level.
The way I did it was by
manually injecting $cookies into the config block, then
using that as a reference within a service.
referring to the service var in all read locations.
'use strict';
app
.config(['$logProvider', function($logProvider ){
var $cookies;
angular.injector(['ngCookies']).invoke(['$cookies', function(_$cookies_) {
$cookies = _$cookies_;
}]);
var _enabled = $cookies.debugMode;
$logProvider.debugEnabled(_enabled);
}])
.factory('DebugSvc', ['$cookies', 'HttpMonitorSvc',function ($cookies, HttpMonitorSvc) {
return {
httpMonitor: HttpMonitorSvc,
debugMode: $cookies.debugMode || 'inactive',
setDebugMode: function (mode) {
var _logEnabled = $cookies.debugMode;
switch( mode ) {
case 'active':
_logEnabled = true;
break;
}
$cookies.debugMode = mode;
this.debugMode = mode;
}
};
}]);

angular provider $get doubts

I have some doubts about providers.
Can someone explain me why I cannot access from the controller the "setText" provider function?
I can only access functions inside the $get block.
var myMod = angular.module('myApp', []);
myMod.controller("mainCtrl", [ "$scope","greeting", function($scope, greeting){
greetingProvider.setText("Hi, ");
}]);
myMod.provider('greeting', function() {
var text = 'Hello, ';
this.setText = function(value) {
text = value;
};
this.$get = function() {
return function(name) {
console.log(text + name);
};
};
});
myMod.config(function(greetingProvider) {
greetingProvider.setText("Howdy there, ");
});
myMod.run(function(greeting) {
greeting('Ford Prefect');
});
Thanks
The setText function is only exposed on your app.config. The only thing you'll have when you access the provider within your controller is what you've included inside the $get function.
For a more detailed answer, check this article:
http://tylermcginnis.com/angularjs-factory-vs-service-vs-provider/
Providers are accessible only in configuration phase of application life cycle. Their specific purpose is to provide a way to configure the future service, which provider should return via this.$get method.
In your case it doesn't really feel like you need a provider, simple service (factory) would have been enough. Or your can make use of factory and add one more method set to change text variable, stored in closure.

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