Best way to handle configuration in AngularJS - javascript

Right now I have a service that runs and is injected into almost every single controller that provides a different API version string and URL string for requests based on $location's information as to whether the app runs on localhost or the website URL. Can something be set up to do something like this instead?
angular.module('app', [])
.config(['$location', function($location) {
// create app constants here and provide them globally somehow without requiring injection
// something like constants.api, module.constants.api, or pretty much anything
// else that exposes the keys set here in a developer-friendly way.
}]);
I am aware that $location isn't available in .config, so the above method won't completely work, but it's just food for thought.
I'm also open to any other suggestions on how to do all the configuration in AngularJS, because I haven't really found a good, clean way to specify app constants without having to use [insert build tool here] and create lots of constants and clutter my DI. I don't think AngularJS was intended to create such cluttered dependency management, but if I'm wrong, someone please correct me and explain a good way to set up better dependency management.

Related

Design pattern for sitewide configuration in Angular JS

I want to set some user preferences in my angular app. I will get these preference from a REST api so I will need to make a http call. Once these have been fetched from the API, their values will not be modified.
What is the best practice in this case?
Can I make http calls in angular.module.value? Or use something like a provider? Or just write a service? What are the things to consider in any of these approaches?
What is the design pattern to be followed in this case?
You could create a service using the factory recipe. That will make the code a lttle cleaner and more modular.
Following that, you could put the user preferences on the $rootScope. This is a good case for a valid use of $rootScope, since your values are constants with application-wide applicability.

Avoiding angular dependency injection with use of global object

I inherited some code that is using a global object to to store angular services. These services get attached to the global object in the run function of the angular module. My question is, can this lead to trouble down the road? What sort of trouble does this cause for testing? Passing around services like this seems a lot easier than injecting all of the services in each controller so I see why this is done. What are other arguments for not doing this? Here is some code to illustrate what I am talking about:
// vars
var globalObject =
{
ng: {},
};
// Setup module
var myModule = angular.module("myModule", []);
myModule.config(doStuff);
myModule.run(setUpGlobals);
// Setup app globals
function setUpGlobals(ngRootScope, ngHttp, ngTimeout)
{
globalObject.rootScope = ngRootScope;
// angular services
globalObject.ng.http = ngHttp;
globalObject.ng.Timeout = ngTimeout;
}
setUpGlobals.$inject = ['$rootScope', '$http', '$timeout'];
Modules and DI were introduced in Angular exactly to avoid relying on globals and improve modularity.
This is naive approach that only works if there is a single module and a single application instance. It will fail if there are several modules that can be used separately (including tests). It will produce awful bugs if there is more than one application instance on the page (for example, if Angular is used for non-SPA applications).
A monolith module hurts testability. Even if it is used like that, some options will be unavailable, e.g. injecting spied or stubbed services in $controller(...) - because a controller relies on globals.
setUpGlobals results in eager service instantiation. This may not be a problem for core services but will be a problem for services that don't need to be instantiated right now.
Less important concern is code size in minified application. ng.$rootScope can be minified to a.$rootScope but not any further. Annotated function should mention '$rootScope' string once, but $rootScope variable name can be minified to a. There will be improvements if a service is used more than once inside a function.
There's a lot of reasons why global variables are bad. Some of them won't be applicable in this case, other ones will.
This makes testing nightmaraish. Dependency Injection is great, because it means you can do some atomic tests, by mocking out the services you don't need. In a simple, non-convulted example, imagine a service that makes API calls via http, if you DI it in, your test can mock out the http and just fake a return, letting you test only the bits of code you want, and saving you from having a test that relies on the API, or worse a test suit that uses up your API calls. With the provider in the global scope, that's much more difficult to achieve.
Just one reason, i'm sure there are many others.

What is the correct place to inject/load a service that contains self-registering components?

I am working on an AngularJS application that has some self-registering components. Concretely, there are some services that do not directly offer any public interface themselves; they merely register some internal objects in some directory provided by another service.
You can imagine the body of such a service's factory as follows:
var internalThing = {
// members ...
};
thingRegistryService.registerThing(internalThing);
return {};
Thus, I only need to ensure that the service gets loaded at some point.
The dilemma I'm facing is as follows: As the service provides no public functions and just needs to "be there", there is no reason to inject it anywhere. As it does not get injected, it never gets instantiated. As it never gets instantiated, the components within the service never register themselves, though.
I can inject the service the usual where in some service or controller that I know will get loaded - but then, I am basically leaving an unused argument in the code (which, if it is the last argument in the list, will even get outlined as an error based on the project's JSHint settings).
Alternatively, I can do the self-registration in a method in the service and call that wherever I inject the service. This would make the service injection "useful", but in turn I'd have to deal with multiple calls myself instead of relying on the built-in singleton mechanism of AngularJS's services.
Or ... should I go yet another route, by providing a method like registerThing somewhere that takes the service name as a string, and that will internally just invoke $injector.get? Of course, that evokes the question again where the correct place to put that kind of call would be.
A little background: This is part of a large project developed in a large team. Build and/or deployment magic somehow handles that any JavaScript code file committed to our VCS by any developer will be available to AngularJS's dependency injection. Thus, any JavaScript that needs to be added has to be provided as some kind of an AngularJS service, controller, etc.
What is the proper way to load such a self-registering service?
Right place to init your module is angular.module('yourModule').run block.
In case of "self-registering" I think it is better to have some implicit method for this:
angular.module('yourModule').run(['service'], function (service) {
service.init();
})
If your build system magically provides all JS code, is an import needed?
A self-registering architecture as your build system suggests should not require imports. The import JSHint errors are the cost of this magic.
I've used a similar behavior with namespace-like design. It can be used for self-registering techniques, although imports get tricky and does not work well with ES6 modules.
This is close to my point: http://blog.assaf.co/automating-component-registration-in-angularjs/

Why would you ever use the $rootScope?

Ok so I was reading here
basically when I have this
MyApp = MyApp || {};
MyApp.settings = {
isFooEnabled: false
}
if I use the rootscope and want to check if isFooEnabled I have to inject the rootScope into whatever object I want to do the check.
How does that make sense?
What is the advantage of using $rootScope.isFooEnabled over using straight standard javascript MyApp.isFooEnabled?
what is better for what?
when should I use one over the other?
The $rootScope is the top-most scope. An app can have only one $rootScope which will be shared among all the components of an app. Hence it acts like a global variable. All other $scopes are children of the $rootScope.
The rootScope's variable is set when the module initializes, and then each of the inherited scope's get their own copy which can be set independently.
NOTE:
When you use ng-model with $rootScope objects then AngularJS updates those objects under a specific $scope of a controller but not
at global level $rootScope.
The $rootScope shouldn't be used to share variables when we have things like services and factories.
Finally, Angular FAQ says this at the bottom of the page: "Conversely, don't create a service whose only purpose in life is to store and return bits of data." See from here.
Actually, I would argue that you shouldn't use $rootScope in this case, you should create a separate service (or factory) that stores your settings, however, the usage and reasons are the same.
For simply storing values, the primary reason is consistency. Modules and dependency injection are a big part of angular to ensure you write testable code, and these all use dependency injection so that unit tests can be written easily (dependencies can be mocked). Whilst there are not many obvious gains from injecting a simple object, it's consistent with the way more complex code is accessed, and there is a lot to be said for that. On a similar note, if you were to upgrade your settings object to fetch data from the server (e.g. for environment specific settings), you might want to start unit testing that functionality, which you can't really do properly without modularising it.
There is also the (frankly weak) namespacing argument - what if another library you import uses window.MyApp?
TL;DR: It's a strongly recommended best-practice. It might seem a bit clunky now, but you'll benefit from doing it in the long run.

Why wouldn't you use explicit annotations when defining controllers in AngularJS?

I am new to AngularJS and learning about the two styles of writing controller functions. It seems as though the only reason someone would not use explicit annotations is to save time, which doesn't seem like a good reason. And being able to minify/obfuscate code seems like a requirement I would want to keep in any application.
Also note that I am not asking which is better or asking for a debate. I am asking for what reasons (or in what situation) it would be more beneficial to not use explicit annotations.
Example of what I'm talking about:
module('myApp').controller('MyController', function($scope) {});
vs.
module('myApp').controller('MyController', ['$scope', function($scope) {}]);
The inline array annotation is simply a workaround over Javascript limitations to allow Angular code to be minified and not to stop working. But it isn't a great solution because if forces you to duplicate your code. And we all know how bad duplicate code is. Angular documentation itself acknowledges that:
When using this type of annotation, take care to keep the annotation
array in sync with the parameters in the function declaration.
It's way too easy to add a new dependency and forget to add the corresponding annotation. Or to reorder the arguments and forget to update the list of annotations. Trust me. Been there, done that.
Fortunately there are tools developed by smart people that take that burden off our shoulders by taking care of annotating the code automatically. Probably the most known is ng-annotate, as mentioned by #pankajparkar. All you have to do is plug it into your build process and your code will be correctly annotated.
To be honest, I find it really odd that Angular documentation recommends against following this approach.
Obviously You need Array annotation of DI while doing JS minification,
If you don't want minification of JS files then you can continue
function pattern.
Refer this Article which has good explanation about what you want.
If you don't want to include array annotation of Dependency injection then simply you could use ng-annotate library. (As you are saying, its not bad pattern thought you can avoid it by ng-annotate)
Which does do convert your code to array annotation of DI while minifying js files
Only you need to add ng-strict-di attribute where ever you declared your ng-app directive.
<div ng-app="myApp" ng-strict-di>
I suppose the one situation where it would be useful and indeed necessary to use the annotations would be if you didn't want anyone to minify your application code.
Using direct arguments
when you pass argument to controller function, In this mechanism, you will pass arguments and angular will recognize
Ex.
module('myApp').controller('MyController', function($scope) {});
module('myApp').controller('MyController', function($scope,$http) {});
OR
module('myApp').controller('MyController', function($http,$scope) {});
In the second and third example, place of $scope and $http is different, but they work without error. This means angular recognizes which is $scope and which is $http.
Now consider you developed an web application and you are going to deploy it. Before deploy will minify your javascript code to reduce size of your js file.
In minification process, each variable will get shorten as like $scope may become a, and angular can not recognize 'a'.
Using array method
This is the standard mechanism, when you pass an array , the array elements are strings except last element, the last array element is your controller function.
You must need to specify the order of each argument in array as string and you can pass that variable in function, here you can provide any variable names as they are just alias.
ex.
module('myApp').controller('MyController', ['$scope',function($scope) {}]);
module('myApp').controller('MyController', ['$scope','$http',function(myscope,newhttp) {}]);
Now in this mechanism, if you use any minifier to minify this js, it will minify and change myscope to any other name say 'b', but it will not touch $scope as it is a string, and you dont need to worry as b is just a alias for $scope.
This is recommended, but if you are using grunt/gulp for minification, you can check these
ng-di-annotate
ng-annotate
The only real reason is that it's faster when mocking an application.
This is a leftover from early on in Angular, when they had a few features that they used for 'showing off' how easy it was to make an Angular app. Another example is how Angular used to look for controllers that were declared globally on window; they removed that 'feature' in 1.3. This changelog best explains why.
It's a fun little gimmick, and it helps ease new developers into the world of Angular, but there's no good reason to use it in a production app.

Categories

Resources