I have a search web app made with AngularJS, which includes two angular modules: the search-keyword-input module and the search-action module.
The first search-keyword-input module has suggest functionality, form validation functionality, and so on. The search-action module's functionality is requesting data and showing sortable results in a table. So each module has its own controller and directive.
Here's the problem: the reason I modularize is, I want modules that could be reused and decoupled. But in this app, the keyword model value in the search-keyword-input module has to be passed into the search action module (the search-action module has to know what keyword to use to perform the search). So how can I pass the value between modules? How can they communicate without being coupled?
I have figured out a way like adding a parent controller and global $scope value:
<div ng-controller="globalController">
<div ng-controller="keywordController"></div>
<div ng-controller="searchController"></div>
</div>
So the search-keyword module could set the keyword value on the globalController's $scope and the search-action controller could also obtain that keyword value from the globalController. But I think this makes the modularization meaningless because they are coupled together. What else can I do?
Your best bet is to create a custom service that can be shared by both controllers, and also used individually, rather than polluting the parent scope.
Related
We want to create a reusable library/module for our ACL functionality. Basically, to check if the logged-in user has access to the particular UI resource and also has permissions to perform actions like click etc... So, we want to expose a hasAccess() function which can be called from inside directives like - ng-show, ng-hide, ng-disabled etc...
We explored few options like angular-acl (https://github.com/mikemclin/angular-acl). With this solution, in every controller, we need to add a line like this: $scope.can = AclService.can; which is bit pain, given that we are having many controllers in our application.
So, was exploring on what is the best way this can be achieved? Is there a way we can export global functions from inside our module service, so that the same is accessible in every template?
Some of the solutions we found was to write code inside module.run() method, and set $rootScope.fn = . But, can we write a run() for a module that is being shared as a library? Is this valid, and if yes, then, when exactly will this run() be called is not clear.
Any idea, what is the best practice solution for such a scenario, where we want to expose a global method/function from inside a service, such that the same can be accessed in templates?
I struggle to find a statisfying solution on how to expose service instances whose methods need to be accessed through multiple parts of my applications.
The situation
First things first, by a 'service', I mean an instance of a function that holds properties & methods which are exposed through an API.
Consider a REST service whose purpose it is to provide convenient methods to access REST points on a server. I would make the following assumptions on that service:
It must be available throughout the application. It is likely that as the app grows, there will be new components that need access.
There is no need of multiple instances of this service. We can consider it a singleton.
My solutions
I can think of 2 possible solutions:
Concatenating scripts & utilizing the global object
I could combine all my script files (e.g rest.service.js, app.js) into a single file and create an object property on the global object (like App).
Then, I could attach service instances to this object. This allows me to do something like this from everywhere within the app:
App.restService.get()
However, even if I wrap each service in an IIFE, i still have to add some variables on window in order to retrieve instances.
Using commonJS / AMD modules
I could require() my service instances from everywhere by using require.js / browserify
The issues
Now I got a headache because on the one hand, people are telling me that polluting the global object is bad practice. Singletons are bad practice also.
On the other hand, we make a lot of effort to 'uglify' scripts, each byte saved considered an enhancement. Using browserify would lead to the same script injected in multiple files, though (I'm using web-components, therefore I've got a lot of isolated scripts). Not mentioning the fact that I have no idea on how to provide a state-safe service using browserify.
So how should I approach this problem?
How should I expose standard services that may or may not be instantiated multiple times? How should I implement state-safe ones?
Just a starting point (but too long to be a comment) I really enjoy the strategy used by AngularJs, where you always instantiate services within a container - and every time you instantiate something you also specify which modules should be injected into it:
angular.module('myApp.services', []); // the second argument are the dependencies (an empty array
At any point, you can retrieve your modules and add functionalities:
var services = angular.module('myApp.services');
services.factory('yourServiceName', //
['other', 'service', 'dependencies'],
function(other, service, dependencies){
other.doStuff();
service.doStuff();
dependencies.doStuff();
[..]
});
You can then inject your module in other modules
var myApp = angular.module('na', ['yourServiceName'])
In angular, the app is instantiated by the framework itself - but I guess you can develop a entry point for your app, so that you can use your services.
..unfortunately, I do not know exactly how this pattern is implemented - probably all the modules are stored within an instance of the application, so the global namespace is not polluted.
This problem also confuses me a lot, I think there are two points I can figure out:
1) There must be an entry point for each service in global, otherwise it is impossible to get the one you need everywhere. It's not good to add many things in global, but I think service reference is the one deserved.
2) Config the service object other than initialization, for example, they can be only one ajax service object with different configuration to do different things. There are objects, so they can be merged and extended.
This is an interesting topic, I would like to see more opinions about, not just management of services, also other resources like templates, objects, files, etc.
Note: I'm not seeking the differences between the value, factory, service, and provider 'recipes', as explained here.
I'm looking for clarification on the different ways to define them: Correct me if I'm wrong, but it appears as though
myApp = angular.module('myApp', [])
.value(...)
.factory(...)
.service(...)
.provider(...)
map to
$provide.value()
$provide.factory()
$provide.service()
$provide.provider()
And you can use either way. I asked this question, and realize I can use $provide as a dependency to module().config(). My questions:
When/why would I use angular.module().provider() vs using the $provide dependency?
Is there any way (or reason) to access/change a provider after definition?
Using AngularJS Batarang for Chrome, I'm looking at the various angular $scope properties, and don't see $provide or $injector. Where do these live?
The provider methods off the module definition are just short cuts. Use them as often as you like because it leads to shorter, easier to read and understand code. Less ritual/ceremony is involved than injecting the $provider service and calling that directly. The main reason to use $provide directly is to access a method on it that is not a short cut from module (such as the decorator) or if you have to do something from within a service or component that is not up at the module definition level.
The common case for changing a provider after it's definition is when you are integrating a third-party component and want to add or change the behavior. The third-party module will define the service and then you step in and override or extend it in some way that is specific to your app. A common case for example is to take the built-in Angular exception handler and extend that to interface with your own components.
$scope is a special "glue" used for data-binding and only exposes properties/functions that you explicitly set on the $scope. All of the other miscellaneous modules/services are stored within Angular's dependency injection container. The very first thing Angular does is create an $injector instance to keep track of dependencies. Therefore $injector === $injector.get('$injector'). Same with $provide. Anything prefixed with a $ is by convention a service that Angular places in the $injector for you to use.
I'm looking for guidelines about when and why should I use the "require" option in the directive definition,
why not communicate just using the Scope - like most of the times in Angular ?
How is it, that suddenly in directives, I ask for the controller itself and not just attaching things to the scope(s) ?
Generally speaking - there are many ways to communicate between directives/controllers/scopes in Angular -
Scope inheritance.
RootScope "emits".
Services (factory/service/provider).
Requiring controllers in directives.
Requiring new Scope/ isolate scope / "normal" scope in directives.
More?
And while I understand how they work technically, it's not clear to me what are the guidelines to decide which one to use and why.
Will be happy for some general/high level guidelines. Thanks.
require is particularly useful if you want to create custom form controls (see section Implementing custom from controls) -- i.e., you want to create your own form control with a directive. You can require the ngModelController to get access a lot of existing functionality via its API/functions.
Another use case is found on the AngularJS home page, section Create Components, where the pane directive uses require: '^tabs' to get access to the tabs controller. Since both of these components/directives create isolate scopes, scope inheritance is not an option. A service wouldn't be a good fit either, since your app could have multiple tabs directives. So, in this case, the controller is used as a means for the pane directive to be able to affect the tabs scope -- to be able to get at the scope. Without using require, the pane directive can't get at the tabs scope. I discuss this in more detail (with a picture) in this SO answer: 'this' vs $scope in AngularJS controllers
require can only be used if the controller-of-interest is defined on the same element (e.g., ngModelController), or if there is a hierarchy (e.g., pane -> tab).
What is the difference between implementing a $rootScope function and a service? Security wise or performance wise.
I have read this, so I am wondering.
I have been trying to figure out whether or not a certain global function for my app would be best implemented in a service or on the $rootScope itself. To pitch you guys with an idea of what I am making, I'm currently developing a dirty form function in which it prompts the user if he/she navigates away from a certain form. In this case, I decided to best implement it as a global function, so any hints?
Thanks for the responses,
Jan
In this case I would go for a service to avoid having a global state. all new scopes are created from $rootScope. New controllers or whoever uses a scope will have values of $rootscope available. For instance, if you define $rootScope.validate() and in a controller you define a function $scope.validate() because you forget about the first definition, something will certainly go wrong.
There is an article by Misko H. about this http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
Services are instantianted on demand, whereas $rootScope is created during bootstrap, and can be injected wherever you need them. This is good for testability.
Angular won't instantiate services unless they are requested directly or indirectly by the application.
(http://docs.angularjs.org/guide/dev_guide.services.creating_services)
As #egamonal mentioned Services are more robust way to share common functionality. Not only services are instantiated on demand, they are singleton by nature so once a service gets created, the same instance gets passed around by AngularJS whenever requested. So if something can go on root scope, it can also be implemented using a service.
One think to keep in mind using such global approach is the possibility on memory leak. Instance of JS classes or maybe DOM elements remain in the memory because you have referenced them from you global functions (either in $rootscope or service).