I am pretty new with AngularJS, but was wondering how to create commonly used functions outside the scope of a controller.
For example, I pretty often need to call a function to start a "Loading" spinner (for RESTful calls, etc). I tried adding a showLoadingModal() function as a service, but those only seem good for retrieving data from what I've seen. Do I have to add this function to all of my controllers, or can you create app-level functions somehow?
You could define your common method on $rootScope, which will be available via prototypical inheritance to any child scope in your application.
Or, you could certainly define a service called "utilities" and just inject it wherever it's needed. Services are good for more than wrapping data-retrieval calls!
Of these two approaches, I would recommend the latter. More testable and less pollution of $rootScope.
Related
Disclaimer : I know there are certain questions which suggest not to access scope inside service or factory but here I am expecting the impact in terms of coding guidelines / whether it is advisable if not then I need proper justification.
We have angular js project and this project is old. Now after refactoring one of my colleague moved the common implementation from directive to service. While doing so , to access the scope of directive he manually started doing as below :
angular.element('<test-dir></test-dir>').scope();
What I felt is this is not the proper way to write the service/factory. I felt we are making the things complicated and suggested to remove the above part of code.
To justify the same I told :
1. This will make unit testability complicated and now we are trying to test the service the way we used to test directive.
2. And we are making this service tightly coupled with directive.
3. Service is not meant to access the scope.
But I think I am not able to convince him as I don't have much point to justify it. Can someone please suggest if I my understanding is correct and give proper justification to convice him. Thanks!
No, the services/factory are supposed to be working with data and should have the logic to process the data provided to them. Preferably, they should not refer to DOM objects or scope variables.
I personally believe that passing $scope to a service is a bad idea, because it creates a kinda circular reference: the controller depends on the service and the service depends on the scope of the controller.
On top of being confusing in terms of relations, things like this one end up getting in the way of the garbage collector.
A Service is just a function for the business layer of the application. It acts as a constructor function and is invoked once at runtime with new, much like you would with plain JavaScript (Angular is just calling a new instance under the hood for us).Use a service when you want to just create things that act as public APIs.
The service class should work on the data provided by the controller, making it reusable at any other place if needed. Also keeping the scope away from it, keeps the code lean and clean thus better maintainability.
I prefer to put a domain object in the controller scope and pass that to the service. This way the service works irrespective of it being used inside a controller or maybe inside another service in the future.
Now, I know global variables is an anti-pattern, and perhaps more so in an Angular world than in other places... however, I have come across a use case where I'm leaning towards breaking the rule...
I have read the comments here, and strongly agree with all the posters who point out the anti-angular'ism my idea represents and the anti-pattern it generally is...
Angular JS - Make service globally accessible from controllers and view
However, now for my use case, I have a $theme service which contains a lot of variables and constants, such as paths to images and strings.
We also have a framework of components which are all made accessible to application designers via directives, allowing most of our applications to be build using just our internal framework in the form of markup directives.
I could make directives for each of the needs, however it would sacrifice the flexibility of having low coupling between views and themes, by allowing developers to access the $theme service directly in the views, without having to (re)write controllers (or even create controllers for everything) or implement new directives, it would allow them to work purely in the markup made available to them by the internal framework. Basically I want to enable application designers to be able to work purely in markup.
We also have made directives which wrap references to ngResource and exposes the methods of the resource on the current $scope as well as directives which binds the current $scope to variables on the $stateParam service, etc, etc, hopefully you get the picture...
e.g.
<h1>{{$root.$theme.NAME}}"</h1>
<img my-src="$root.$theme.logo.url" />
instead of lots of code with controllers and directives...
<my-theme-name />
<h1 my-theme-name />
<my-theme-logo />
<img my-theme-logo />
The difference between these two approaches is that the ones where the directive is attached a primary tag, it's purely a decorator, where in the former cases where the directive is the primary tag, the directive template will determine the resulting primary tag. A slight difference yielding a larger degree of flexibility in the approach where we are using the directives purely as decorators and not information experts.
However, instead of having to write directives all together, perhaps for things which are purely added to the individual theme, just to be used in a single app, in which case adding support in the $theme service seems to be wrong approach...
However, going with a strict custom directive driven approach, it will possibly allow us to more easily port to e.g. Angular2 or even something else by simply building an adapter or transpiler, without having to rewrite any of the markup, but simple rewire the directives to new controllers and handlers written in the new library.
So, back to my question, provided we work around this caveat for the testing, would it be a viable approach, or is it a slippery road to global variable hell where soon we will have polluted our $root with tons of things because it was the easy way ?
_____ Update 7th May 2016 _____
OK, I decided against making my theme service globally available when I suddenly was compelled to put my $fileSaver service out on the global scope as well, indicating the my fears of "scope-creep" were valid, instead I decided to implement a <my-injector my-name="$theme" /> directive which allows application designers to import services declaratively, the directive makes the injection target specified in the myName attribute available on the current scope.
It allows app designers to use the service anywhere in the scope like this:
<my-injector my-name="$theme" />
<img ng-src="$theme.logo.url" />
I thought about creating difference convenience directives, e.g. some app designers might find <my-import my-name"$theme" /> more intuitive, but that is a different discussion...
It's not an absolute necessity to avoid rootScope. From the Angular FAQ, with my emphasis:
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
Since your theme seems to be largely data, I say why not use root? If it does contain functions, is it possible to split it? I can't decide that for you, I haven't seen what you're actually trying to do...
Singletons are a bad idea for the most part. I believe everyone is on board with this concept. I am wondering why does Angular rely so heavily on factories for its dependency injection considering factories are singleons ? Why are singletons not a bad idea in Angular ?
Singleton is not considered a 'good practice' when accessed directly, through global class name (in java) or as a global variable. Your code is coupled with it and difficult to reuse and unit test. Changes in one class/module can cause a side effect in another one without you being able to execute those modules independently.
In Angular it is injected as a dependency - a function parameter. So it is a different thing. You can easily pass different implementation of you singleton to one function and different to another (if you wanted). Your code explicitly declares it as dependency and allows the client (caller) to pass whatever he wants instead of hiding it internally.
What is the best way to use a third party library in Angular without exposing a global variable?
For instance, if I were using underscore.js, I want to inject _ into only the controllers that use it.
angular.module('module').controller(function(_) {
// _ is injected only into this scope
};
To get this effect, I've seen some people load underscore globally with a script tag, then create a service like this:
myModule.factory('_', function ($window) {
return $window._;
});
However, this still pollutes the global scope with _.
Is there an 'Angular way' of registering and injecting third party libraries without causing this problem?
You can't do anything about how the third party libraries are written, except by forking them and creating your own version.
Third party libraries will almost always create such variables on the global scope, and you mention the two most frequently used ways of using them in an Angular module: by injecting $window and using them directly, or by creating a thin service which provides the object.
I would favor the latter in most cases because it makes the code in other modules simpler. Also, in my mind, the service approach makes the code in your controllers seem less 'magic' and more debuggable, as it is clear where the library, e.g. _ comes from. Variables appearing "out of thin air" is some of the problem with global variables to begin with.
The library will still "pollute" the global scope, unless you explicitly deletes them from the global scope, perhaps after fetching a pointer to them in a service, which then can pass it on. However, I can't see any motivation for doing so. Any other attempts, such as using an Angular service to actually download the third party script and evaluating it, somehow preventing it from reaching the global scope, seems vastly inefficient, over-engineered and slow (both computationally and with regards to when HTTP requests are fired).
It's otherwise a good idea not to create any more global variables than strictly necessary.
For third party libraries that support provide a noConflict function - such as Underscore and jQuery - you could use that to prevent global pollution in your Angular factory.
myModule.factory('_', ['$window', function ($window) {
var underscore = $window._.noConflict();
return underscore;
}]);
Since all services in Angular are singletons it means that the injector uses each recipe at most once to create the object. The injector then caches the reference for all future needs.
Put third party libraries in angular constants.
Keeps your code readable and clean and you can inject them in the specific controllers, directives,... where you want to use them.
angular
.module('sampleApp', [])
.constant('_', window._)
.constant('$', window.$)
As documented in John Papa's style guide [credits to #MaximilianoBecerra]
Miike is right, you can't "stop" a module from doing badness. Once anything calls window.whatever = ...
Require.js though is cool as async module definition library/methodology which helps people who are building packages do it without mucking in the global namespace. It won't help offenders, but it's neat.
http://requirejs.org/
I've created a tool which will create an angular factory rather than a global for all libraries that support require js. Since most libraries support require.js, I was able to write a function which mimics require's 'define' function, but rather than adding the library to require, it adds it as an angular injectable. It is practically plug n' play. Just include the js source after jQuery and angular and you're good to go. It's called angular global injector and available on github. Here's the link: https://github.com/btesser/angular-global-injector
I'm new to AngularJS. I'm currently looking at the services. Some of these services look like the replace functions already available in JavaScript. For instance, the $timeout service. Why does AngularJS have these services? Is there a technical reason? Are there any advantages to this approach? I just don't understand the need for using these services.
Thank you for any help.
A service is a function or an object, with a set of methods, that could be used by several components (controllers, directives, filters, other services) of your application.
The main advantage that "wrapping" services like $timeout or $window have over their global functions equivalent is that they're injected by Angular, and can thus be mocked in unit tests, i.e. replaced by fake implementations.
Read the chapter about dependency injection in the angular documentation. Regarding the $timeout service in particular, its documentation explains what it does in addition to the native setTimeout() function.
Angular uses dirty check -- it keeps track of all variables that should be updated automatically and in case of any changes iterates through them as many times as needed (as there can be interdependence between observing variables). (To be precise if there are too many iterations something wrong is probably with your code and exception is raised).
The main reason behind $timeout is to signal to Angular engine that you will be making some changes to the observing variables in the callback. In case you would call normal timeout there is a way to inform Angular about those changes by calling e.g. $scope.$apply(function() {...}).
The other big reason, as #JB Nizet stated is that mocking is much easier.
Service is a way to store persistent information over your application.
unlike the $scope, which is an new child of $rootScope per each route\controller.
The purpose of the Angular-specific implementation of already existent functions, is to make your life and work easier, by automatically telling angular to register your changes, and apply them to the current scope at the next $digest iteration.
I'll be happy to address any other questions.
Good luck with Angular! :)
I will now create an example plunk to emphasize the idea to you
UPDATE:
This is the plunker:
http://plnkr.co/edit/mVmhZpEdsZDQ0MUQpFJ7?p=preview
I used ng-change and the native change
keyup event to explain the point.
while using the angular built in ng-change directive requires no additional effort nor code, using the native events requires wrapping everything in the scope.$apply function/
calling scope.$digest manually.
(run the code and see for yourself)
Check out this older answer which covers Services, Providers and Factories.
AngularJS: Service vs provider vs factory