In my Angular app, I have some resource modules, each containing some cache factories.
For example,
projectRsrc.factory('getProjectCache', ['$cacheFactory', function($cacheFactory){
return $cacheFactory('getProjectCache');
}]);
I have a few of these to cache values received from the servers.
The problem is that at times I'd like to clear all the caches. So I want to put all the cacheFactories into one CacheCentralApp module and delete all the caches with a single call.
The trouble is, I don't know of any way to access other factories inside my module. So for instance, if I create a module CacheCentralApp, and in it, declare factories that provide cacheFactorys, how can I create a function in there that calls removeAll() on every cacheFactory?
I don't think it is possible to target all the factories of a certain module. I think however that another solution to your problem is to send a event that all factories has to be cleared. This will prevent that you will have to loop through all your factories and call a .clear() function on everyone.
You could send a event request with the following code:
$scope.$broadcast('clearAllCaches');
And listen to this event in every factory with:
$scope.$on('clearAllCaches', function() {
clearCache();
}
In a separate module you might create a factory for that:
var cacheModule = angular.module('CacheCentralApp', []);
cacheModule.factory('MyCacheFactory', ['$cacheFactory', function($cacheFactory) {
var cacheKeys = [];
return {
clearAll: function() {
angular.forEach(cacheKeys, function(key) {
$cacheFactory.get(key).removeAll();
});
},
get: function(key) {
if(cacheKeys.indexOf(key) == -1) {
cacheKeys.push(key);
return $cacheFactory(key);
} else {
return $cacheFactory.get(key);
}
}
}
}]);
To create new or get existing Cache you simply call MyCacheFactory.get(cacheName). To clear all the caches ever created in the factory you call MyCacheFactory.clearAll().
Note: I am not quite sure that Array.indexOf is available in every browser, you might want to use Lo-Dash or another library to make sure your code works.
Related
I am trying to use the same service for different modules. There are many modules so i tried to inject them in a parent module. Something like this:
var app=angular.module('myapp',['module_1','module_2',....,'module_n']);
var module_1=angular.module('myapp1',[]);
var module_2=angular.module('myapp2',[]);
var module_3=angular.module('myapp3',[]);
.
.
.
var module_n=angular.module('myappN',[]);
and the service which is common to all the n modules is like this:
.service('myService',function(){
...doing something here...
});
Now I am not able to figure out how to use this service for all the submodules.
With which module should I associate this service ?
I tried doing app.service('myService',function(){...}), but it did'nt work.
Where am I going wrong?
EDIT 1:
Moreover I am trying to share a variable with all these submodules using the service. I am not sure if, I am doing the right thing by using a service for sharing variable or should I use a Provider or Factory for this job.
EDIT 2:
I found these links, but I could not grasp the answer. Refer to them and please provide my answer
How to share a variable between multiple modules in AngularJS
Passing variable between controllers which are on different modules
Lets suppose you want to build a Service to share a certain variable between two Controllers. You should be able to use your Service doing the following:
MyService.js
// Lets suppose you want to share a certain variable between controllers
angular
.module('myApp')
.service('myService', function () {
// If you wish you can inject and use $scope
var vm = this;
// Variable to share
vm.sharedItem;
// Method to set a certain value into a variable
function setItem(item){
vm.sharedItem = item;
}
// Method to get that variable
function getItem(){
return vm.sharedItem;
}
// Exposing your methods
return {
setItem : setItem
getItem : getItem
}
});
SetController.js
angular
.module('myApp')
.controller('SetController', SetController);
// Inject your Service
function SetController(myService) {
var vm = this;
// variable used to set the value
vm.setMe = 'hello';
// Call `setItem()` method from `myService` -> sharedItem will get setMe value
myService.setItem(vm.setMe);
console.log("Set shared item "+vm.setMe);
};
GetController.js:
angular
.module('myApp')
.controller('GetController', GetController);
// Inject your Service
function SetController(myService) {
var vm = this;
// variable used to get shared the value
vm.getMe= null;
/* Call `getItem()` method from `myService` to get the shared
* value and assign it to `getMe`*/
vm.getMe = myService.getItem();
console.log("Got shared item "+vm.getMe);
};
I remind you can access this.var in your view using controllerName.var. It is a good solution to make sure you are using a certain controller. You can always use $scope if you wish.
I hope I've been helpful.
I'm using Angular to develop commenting functionality for a web app.
Currently there are two sections in the application were a user can comment:
Category
Product
About 90% of the commenting functionality is the same for both sections and as such I would like to make this reusable - i.e write some service or controller that I can reference/use as a base.
So far, my research seems to point to using a factory service but unfortunately this doesn't seem to work (I've spent the whole day running through various tutorials).
It is quite possible that I am over thinking this and making it far too complicated but I honestly don't know which way to turn anymore.
Herewith a quick and dirty overview of what I have so far:
HTML view for the category
Controller for the category (receives data from service and posts data to service in order to bind data to model)
Service for the category (retrieve and stores all the necessary
data)
The product uses the same logic and a lot of the code in the service and controller will be duplicated.
I've merged the two services into one service successfully but I'm having trouble doing the same for the controller.
Do I:
Write a base controller that will communicate with the above mentioned service and that will hookup with the two existing controllers
OR
Write a factory/provider service that hooks up to the two existing controllers as well as the above mentioned service.
If you go the route of using a factory, you could put all the common functionality into its return object and reference that from your controllers.
Factory
angular.module('myModule').factory('CommonFunctions', function(){
return {
foo : foo,
bar : bar
}
function foo(){
console.log('foo');
};
function bar (){
console.log('bar');
};
}
Controller
angular.module('myModule')
.controller('myController', ['CommonFunctions', function(CommonFunctions) {
var vm = this;
vm.foo = CommonFunctions.foo();
vm.bar = CommonFunctions.bar();
}
angular's separation of service types ie:
for specific values
constant
value
(constant for specific values needed before other services are created)
for functions
factory
service
provider
(provider for specific instances when you need a services before other services are created, usually taking advantage of constants)
allow the ability to share data and ways to process that data between controllers and or directives, anything that can be a value can also be a constant, the only difference there being where they can be injected. Similarly any service can be rewritten to a factory or a provider, it is more your specific use case / what your more comfortable writing that would determine which to use, but really the best way to think about it is if you have a value that needs to be shared but is not needed inside angular.module.config then use value, otherwise use constant, now if you have a single function that you want to share, (maybe it processes that value in some way or maybe it just does something else) you should write it as a factory, then when you have a few of those factory's that deal with either that value, or anything else, you can combine them into a service or configure and combine them using a provider. here is a simple example (note i am using the recommended syntax for writing angular services):
'use strict';
var app = angular.module('test.app',[]);
app.constant('configureableValue',{defaultValue:55});
app.value('editableValue',{defaultValue:100,editedValue:null});
app.provider('configureValue',configureValueProvider);
configureValueProvider.$inject - ['configureableValue'];
function configureValueProvider(configureableValue){
var defaultVal = configureableValue.defaultValue,
originalVal = defaultVal;
return {
getValue:getValue,
setValue:setValue,
resetValue:resetValue,
'$get':providerFunc
};
function getValue(){
return defaultVal;
}
function setValue(val){
defaultVal = val;
}
function providerFunc(){
return {
get:function(){ return getValue(); },
reset:function(){ resetValue(); }
};
}
function resetValue(){
defaultVal = originalVal
}
}
// this factory is an example of a single function service, this should almost always be defined as a factory
app.factory('getEditableValue',getEditableValue);
getEditableValue.$inject = ['editableValue'];
function getEditableValue(editableValue){
return function(){
return editableValue.editedValue ? editableValue.editedValue : editableValue.defaultValue;
};
}
// same with this one
app.factory('setEditableValue',setEditableValue);
setEditableValue.$inject = ['editableValue'];
function setEditableValue(editableValue){
return function(val){
editableValue.editedValue = val;
}
}
// now this is an example of a service service collecting the factorys for an object with all the related behavior we need
app.service('editableService',editableService);
editableService.$inject = ['getEditableValue','setEditableValue'];
function editableService(getEditableValue,setEditableValue){
var self = this;
self.setVal = setEditableValue;
self.getVal = getEditableValue;
}
app.config(appConfig);
appConfig.$inject = ['configureValueProvider'];
function appConfig(configureValueProvider){
configureValueProvider.setValue('i changed '+ configureValueProvider.getValue() +' to this!!!!');
}
app.run(appRun);
appRun.$inject = ['configureValue','editableService'];
function appRun(configureValue,editableService){
console.log('before editing: ',editableService.getVal());
editableService.setVal('changed!!!');
console.log('after editing: ',editableService.getVal());
console.log('we changed this in the config func: ',configureValue.get());
configureValue.reset();
console.log('and now its back to the original value: ',configureValue.get());
}
i know thats a lot for a simple example, but there are a lot of features provided by angular, and many ways to use them, hopefully this helps.
If I user factory in angularjs for $http call then I would use it like below,
app.factory('myDataService', function($http){
return{
getProducts:function()
{
return $http.get('api/Product');
}
}
});
and use it in controller like this,
app.controller('appCtrl',function($scope,myDataService){
//Get Products
myDataService.getProducts().success(function(data,status){
//some stuff
}).error(function(data,status){
//some stuff
})
});
This is a simple way to make call to web api......
But
here i want is to add new layer called myDataService layer which will be helpful to define get, put ,post, delete methods in simple way like this,
Note:(Here I'm giving only concept. I dont know real implementation of it.
I dont know what should i use here .factory .service or javascript simple function. But let's say I'm using factory as below
myDataService
app.factory("myDataService", function (myHttPLayer) //myHTTPLayer is injected
{
//below code is not correct (I want to connect it to myHTTPLayer somehow)
return{
var myVar={
get:{
products:'/api/Product',
companies:'/api/Companies'
},
post:{
product:'/api/Product',
company:'/api/Companies'
}
}
}
});
here what i want is to introduce new layer like this which only gives information about get,put,post methods and api calls....
I don't want to write $http.get, $http.post and all everytime in angular controller. I want to write them at one place only one time. like this,
some.js...
myHTTPLayer
app.factory('myHttPLayer',function($http){
return {
get:function()
{ return $http.get(url);},
post:function(obj)
{ return $http.post(url,obj)
}
});
I want to connect both factories and by using them or writing them at angular controller side, i want to make web api call.
Note: This is just a concept.
please help me ....
In my mind what i want to implement is,
in angular controller something like,
app.controller("appCtrl",function($scope,myDataService){
//Connect myDataService to myHTTPLayer internally so I can use them as below....
//I want my api to be called when i write myDataService.getProducts() shown as below ....
myDataService.getProducts().success(function(){})
.error(function(){});
})
A big note : edit
Dont consider single line of code of myDataSevice. Its totally wrong. Just consider that i have all posible http methods in one factory with their proper prototyes which can return promise in myHTTPLayer. I will not temper it , once it is written correctly. What i want to do is to write myDataService code such a way that i can use it in angular controller without using myHTTP layer stuff in controller. When i use myDataService in controller, it must use httplayer behind the scene and it must make angular http call to defined web api. This is just a concept to be implemented.I want to develop such architecture. But unable to find the answer.
#stackg91 posted a great answer, but I believe it can be simplified further with regard to the else block. The below will work no matter what your 'variable' is ('products', 'company', etc.).
app.factory('myHttPLayer',function($http){
return {
get: function(variable) {
if(variable == null){
return $http.get(url);
} else {
return $http.get(url + '/api/' + variable);
}
}
}
});
Then you could simply call myHTTPLayer.get(products) in your controller for example. I don't believe you need three layers to get the functionality you're looking for here. I think a controller which passes a parameter to a service making '$http calls' utilizing that parameter is enough.
I normally do it like that, hope this helps you
app.factory('myDataService',function($http){
return {
getProducts:function(variable, callback) // this variable can be products or companies
{
if(variable == null){
return $http.get(url).success(callback).error(callback);
}else if(variable == products){
return $http.get(url+'/api/Product').success(callback).error(callback);
}else if(variable == companies){
return $http.get(url+'/api/Companies').success(callback).error(callback);
}
}
});
In COntroller it would look like this
$scope.test = myHttPLayer.get(products).onsuccess(function(){
Do Something ....
});
I have this service I'd like to use, but it could happen that some parts of my application require the same service but with different parameters to use internally. Since services always refer to the same instance as far as I understand it, this would mean either manually having to edit the parameters everywhere before performing an action, or passing them in to every function. This would get messy very fast and I was wondering if it was possible to create a factory to initialise the different versions of the service I need.
Something like this, for instance:
function FooService(){
this.setParams = function(params){
...
}
};
function FooServiceFactory(){
return{
createService: function(name, params){
var serv = new FooService();
serv.setParams(params);
ServiceModule.service(name, serv);
}
}
};
You can create factories as well.
app.factory("InstantiateMe", [function() {
return function(params)
{
// some code
};
}]);
app.controller("Ctrl", ["InstantiateMe", function(InstantiateMe) {
var instance = new InstantiateMe({param: "value"});
}]);
In short, the factory function returns just that: a factory. Its return value can be an object, a function or, in this particular case, a constructor. You can this way instantiate multiple instances of your desired service.
Here is a nice article on the subject, elaborating on what I've summarised, that you may find useful.
I am fairly new to AngularJS and am trying to learn some best practices. I have things working, but would like to start adding some unit tests to my modules and controllers. The first one I am looking to tackle is my AuthModule.
I have an AuthModule. This Module registers a Factory called "AuthModule" and exposes things like "setAuthenticatedUser" and also fields like "isLoggedIn" and "currentUser". I think this is a fairly common pattern in an AngularJS application, with some variations on the specific implementation details.
authModule.factory(
'AuthModule',
function(APIService, $rootScope) {
var _currentUser = null;
var _isLoggedIn = false;
return {
'setAuthenticatedUser' : function(currentUser) {
_currentUser = currentUser;
_isLoggedIn = currentUser == null ? false : true;
$rootScope.$broadcast('event:authenticatedUserChanged',
_currentUser);
if (_isLoggedIn == false) {
$rootScope.$broadcast('event:loginRequired')
}
$rootScope.authenticatedUser = _currentUser;
$rootScope.isLoggedIn = _isLoggedIn;
},
'isLoggedIn' : _isLoggedIn,
'currentUser' : _currentUser
}
});
The module does some other things like register a handler for the event "loginRequired" to send the person back to the home screen. These events are raised by the AuthModule factory.
authModule.run(function($rootScope, $log, $location) {
$rootScope.$on("event:loginRequired", function(event, data) {
$log.info("sending him home. Login is required");
$location.path("/");
});
});
Finally, the module has a run block which will use an API service I have to determine the current logged in user form the backend.
authModule.run(
function(APIService, $log, AuthModule) {
APIService.keepAlive().then(function(currentUser) {
AuthModule.setAuthenticatedUser(currentUser.user);
}, function(response) {
AuthModule.setAuthenticatedUser(null);
});
});
Here are some of my questions:
My question is how would you setup tests for this? I would think that I would need to Mock out the APIService? I'm having a hard time because I keep getting unexpected POST request to my /keepalive function (called within APIService.keepAlive())?
Is there any way to use $httpBackend in order to return the right response to the actual KeepAlive call? This would prevent me from having to mock-out the API service?
Should I pull the .run() block out which obtains the current logged in user out of the AuthModule and put it into the main application? It seems no matter where I put the run() block, I can't seem to initialize the $httpbackend before I load the module?
Should the AuthModule even be its own module at all? or should I just use the main application module and register the factory there?
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.angularjs docs
I suggest you take a look at this authentication service, using a service is the way to go.
Hopefully this would help ... Good luck