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?
Related
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.
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.
I have been battling this issue for a while now.
In my Ember app (using ember-cli) I have a model that I would like to attach some class methods for.
I would like to be able to call from my controllers Model.allWithIssues to find all of the model instances that have registered issues.
To achive that, I have added a method using the following code:
Model.reopenClass({
allWithIssues: function () { ... }
})
My problem is, I don't have access to the data store from inside the model.
I have tried in many different ways inject store:main to the model but with no luck.
One answer here even claimed there is no way to inject to a model, while others suggested ways to achieve DI into models that simply didn't work.
Is my approach completely wrong? Should I keep this logic in a separated class?
EDIT:
The solutions I had in mind are:
Make a generic controller that includes this methods and have all of the controllers that need this method extend it.
Some sort of mixin
Make a "repository" class, working as an abstraction layer between the controllers and the store.
I'm still not sure which route is the best to go in, but all of those seem too robust for something as simple as this.
Controller is not the right place either, unless you have all models loaded, and what you do its a computed propert.
the model's hook in a router would be a better place, its whitin its responsibilities of an adapter to loa data, deserialize it, and provide you with model instances.
Or you can create a service, that lives in you /lib folder, register it with the CI, and inject the store to it. depends on your use case.
I've been tinkering with AngularJS and I've built up a small collection of directives and services that I would like to package into a single JS file so that I can use them anywhere.
I have some website specific settings that my module will need for API calls and that sort of thing. I'm just wondering what the Angular way of making configurable modules is. Obviously I don't want to have to modify my reusable JS file for each website, as that kind of defeats the purpose of having it. Seeing as the values are going to remain the same for each website it seems like an awful lot of hassle to pass them in as an argument on each function call, and I'd rather stay away from global variables as much as possible.
I've searched a lot of questions for the answers I seek, and the closest pattern I've found so far is to have my reusable module be dependant on a not included module called "settings" or something and then define that module in the page's JS file, allowing the reusable module to pull the values from it. Here's an example to show what I mean.
This seems backwards to me. It's kind of like having a function pull values from global values instead of passing the values in as arguments.
Is this really the best way of doing this, or is there an alternative?
It sounds like you're looking for a provider.
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Here's a very basic example of a provider:
myMod.provider('greeting', function() {
var text = 'Hello, ';
this.setText = function(value) {
text = value;
};
this.$get = function() {
return function(name) {
alert(text + name);
};
};
});
This creates a new service, just like you might with myMod.service or myMod.factory, but provides an additional API that is available at config time—namely, a setText method. You can get access to the provider in config blocks:
myMod.config(function(greetingProvider) {
greetingProvider.setText("Howdy there, ");
});
Now, when we inject the greeting service, Angular will call the provider's $get method (injecting any services it asks for in its parameters) and gives you whatever it returns; in this case, $get returns a function that, when called with a name, will alert the name with whatever we've set with setText:
myMod.run(function(greeting) {
greeting('Ford Prefect');
});
// Alerts: "Howdy there, Ford Prefect"
This is exactly how other providers, like $httpProvider and $routeProvider work.
For more information on providers and dependency injection in general, check out this SO question on dependency injection.
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).