I'm using the $cacheFactory to save some data in my cache and everything was working very good until today that I've decided to separate my cacheFactory into a single class called MyFactory.js. Now I'm getting the error:
TypeError: tableCacheFactory is not a function
Because it's taking the injection as a simple method or something, does anybody know if I'm missing something here?
main.js
angular.module('main-component',
['my-component',
'my-cache-factory'
]);
MyFactory.js
angular.module('my-cache-factory', [])
.factory('tableCacheFactory', [
'$cacheFactory', function ($cacheFactory) {
return $cacheFactory('my-cache');
}
]);
MyService.js
angular.module('my-component', [])
.factory('my-component-service',
['$rootScope',
'$http',
'$q',
'tableCacheFactory',
function ($rootScope, $http, $q, tableCacheFactory) {
function getMyData (prodNro) {
var deferred = $q.defer();
var promise = deferred.promise;
var dataCache = tableCacheFactory.get('tablecache');
if (!dataCache) {
dataCache = tableCacheFactory('tablecache'); // TypeError: tableCacheFactory is not a function
}
var summaryFromCache = dataCache.get('tablecache' + prodNro);
if (summaryFromCache) {
deferred.resolve(summaryFromCache);
} else {
$http({
method: ...
data : ...
url: ...
}).success( function (data, status, headers, config) {
var objectResult = {
"data": data,
"status": status,
"headers": headers,
"config": config
}
if (data.response) {
// Save to cache
dataCache.put('tablecache'+prodNro, objectResult);
}
deferred.resolve(objectResult);
}).error(function (data, status, headers, config) {
...
});
}
return promise;
}
You seem to have some misconception about how the $cacheFactory works.
In var dataCache = tableCacheFactory.get('tablecache'); you are using it like it was a initialized Cache object containing another Cache object.
On the other hand dataCache = tableCacheFactory('tablecache'); uses it like it was the $cacheFactory itself.
And both of them try to access record 'tablecache' in something that I think should already be the tableCache itself.
The error is exactly what it says it is. As per the docs, calling $cacheFactory('my-cache'); does not return a function to create more caches.
It returns a $cacheFactory.Cache object which has methods like put(key, value) and get(key). Use those instead.
I'd change the whole structure of the caching (note that the name of the factory is changed):
.factory('tableCache', [
'$cacheFactory', function ($cacheFactory) {
//Return what the name of the factory implies
return $cacheFactory('tablecache');
}
]);
And then use that without needing to do more weird 'tablecache' namespacing
function getMyData (prodNro) {
...
var summaryFromCache = tableCache.get(prodNro);
...
tableCache.put(prodNro, objectResult);
}
The tableCacheFactory was wrapped inside my-cache-factory module. So you need to inject the module first into your my-component module before using it. So it should be like this:
angular.module('my-component', ['my-cache-factory'])
you defined your cache factory in the module my-cache-factory, but then never injected that module to your main component-service module. Do angular.module('my-component', ['my-cache-factory']) instead.
Related
I was recomended to use Angular services in order to centralize many repetative functions that were store in my controller, so I am rewriting my code using services now.
It seemed simple at first but cant seem to find a good structure to fetch my ajax data (only once), then store it in my service for my controller to reuse the cached data when ever it needs it. At the moment I keep getting errors saying: TypeError: Cannot read property 'sayHello' of undefined.
I believe this is because theres is a delay to fetch my ajax data via my service before the controller loads. Im not quite certain how I can optimize this. Would anyone have a better stucture to this?
Service:
app.service('MyService', function ($http) {
this.sayHello = function () {
var deferred = $q.defer();
$http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(data){
var configurations = data;
var configurations.data_result_1 = configurations.data_result_1.split("\r\n");
var configurations.data_result_2 = configurations.data_result_2.split("\r\n");
deferred.resolve(configurations);
}
return deferred.promise;
};
this.sayHello(); //Run the function upon page laod.
});
Controller:
app.controller('AppController', function (MyService, $scope) {
$scope.configurations = null;
$scope.configurations = function() { MyService.sayHello() };
});
I recommend you to use another way to declare the service:
app.factory("MyService", function($http){
var configurations = {};
$http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(data){
configurations = data;
configurations.data_result_1 = configurations.data_result_1.split("\r\n");
configurations.data_result_2 = configurations.data_result_2.split("\r\n");
});
return {
getConfigurations: function(){
return configurations;
}
}
In your controller you can use a $watch, then when the configurations objects changes you take the information:
.controller("YourCtrl", function($scope,MyService){
var vm = this;
vm.configurations = {};
$scope.$watchCollection(function () { return MyService.getConfigurations()},function(newValue){
vm.configurations = newValue;
});
Totally agree with Bri4n about store configuration in the factory. Not agree about the controller because you said you don't want to watch, but only load data once.
But you $http already return a promise so as Brian said this is nice (just $q is useless here so you can delete it from injection). And I just wrapped http call in function, and the exposed function just check if configurations are already loaded. If yes, just return configurations else load it and then return it.
app.factory("MyService", function($http,$q){
var configurations = {};
function loadConfig(){
$http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(data){
configurations = data;
configurations.data_result_1 = configurations.data_result_1.split("\r\n");
configurations.data_result_2 = configurations.data_result_2.split("\r\n");
});
}
return {
getConfigurations: function(){
If( !!configurations ){
return configurations;
}
//Else loadConfig.then return configurations
}
}
In your controller you can just get config without need to know if it is already loaded.
.controller("YourCtrl", function(MyService){
var vm = this;
// If configurations already loaded return config, else load configurations and return configurations.
vm.configurations = MyService.getConfigurations();
I write on my phone so my code is not perfect I can't write properly.
OK, on second thought, it looks like you are not using the dependency array notation properly. Change your code to:
app.service('MyService', ['$http', function ($http) {
// do your stuff here
}]);
and for the controller:
app.controller('AppController', ['MyService', '$scope', function(MyService, $scope) {
// do your stuff here
}]);
While the question is more I suppose javascript type question more so than an Angular question, I was creating a service in which I would call it like this
// controller injects activityApi , then service function call is made
var activities = activityApi.getActivityById(1122);
I get an error if I do not put "this" keyword in front of function name
1. Why do I need "this."
2. What other alternatives? ( I am trying to get into the habit of not doing old school javascript with function blah() { ... }
this.getActivityById = function(id) {
$http.get('/Umbraco/Api/ActivityApi/GetActivity').
success(function (data, status, headers, config) {
console.log(data);
return data;
}).
error(function (data, status, headers, config) {
// log error
});
};
If you are writing a factory make use of an object inside the factory ,and put all your calls into it.like
(function() {
'use strict';
angular
.module('app')
.factory('activityApi', activityApi);
activityApi.$inject = ['$http'];
function activityApi($http) {
var service = {
getActivityById : getActivityById ,
};
return service;
function getActivityById (id) {
$http.get('/Umbraco/Api/ActivityApi/GetActivity').
success(function (data, status, headers, config) {
console.log(data);
return data;
}).
error(function (data, status, headers, config) {
// log error
});
};
};
})();
here you are returning factory object in which calling function is available.
Here you can call the factory function as
var activities = activityApi.getActivityById(1122);
Regarding angular's services and factories. Both .service and .factory return functions. However, angular's $injector calls the function returned by .service with new keyword (using $instantiate method), whereas it calls .factory function as is using invoke method. Since it uses new keyword for service functions, you have to have this keyword inside your function to reference the object, created by new and returned from the function.
As #NikhilVM shows, you can use factory instead of service if you want to avoid using this. Both service and factory return values are cached, so if the factory returns an object it becomes global. So in this sense it's no different from service using this to add methods. Using factory has the advantage in that you can return a function constructor to be later called to instantiate multiple class instances, while you can't do that with .service.
I'm pretty new to angularjs and I need to do the following:
I have few partial views, each containing simple configuration form, which needs to be fetched/PUT to server.
Each view has a controller, each controller has a corresponding service which is responsible for doing GET/PUT request to a given backend endpoint.
Services differs right now only in endpoint url.
The question is:
how would you avoid the following?
var providerConfigService = function ($http) {
this.fetchConfig = function (endpointUrl, success, failure) {
$http.get(endpointUrl)
.success(function (data) {
success(data)
})
.error(function (data, status) {
failure(status)
});
};
this.updateConfig = function (endpointUrl, config, success, failure) {
$http.put(endpointUrl, config)
.success(function (data, status) {
success()
})
.error(function (data, status) {
failure(status)
});
}
};
var facebookConfigService = function (providerConfigService) {
var facebookEndpoint = "";
this.fetchConfig = function (success, failure) {
providerConfigService.fetchConfig(facebookEndpoint, success, failure)
};
this.updateConfig = function (config, success, failure) {
providerConfigService.fetchConfig(facebookEndpoint, config, success, failure)
};
};
// POTENTIALLY DUPLICATED CODE FOR OTHER VIEWS
// SERVICE REGISTERING
I'm more like a Java guy, so I would do something like this in Java and Spring world:
Provide endpointUrl as a constructor parameter, then either create a Factory class or just declare preconfigured beans.
I'm looking for a similar solution in angular world. Should I use angular's factories/providers? If so, how? It's probably straightfroward, but I'm quite confused with angular's services/controllers/factories/providers.
Typically in the angular world you would try and use ng-resource. If your API's are RESTful and basic I would recommend ng-resource. If not, then it's ok what you've done, but typically those gets and puts would just be within the provide service itself. You're only DRY-ing up the success and failure but the reality is that those would probably be different anyway.
You can use a Factory (read more) and configure it in your controller. You can also improve the flow by using promises instead of passing callbacks.
angular.module('yourapp')
.factory('configFactory', function ($http) {
return {
endpointURL: "", // this is what you modify
fetchConfig: function () {
$http.get(this.endpointURL)
},
updateConfig: function (config) {
$http.put(this.endpointURL, config)
}
};
})
//and your controller
.controller('someCtrl', function($scope, configFactory){
configFactory.endpointURL = 'http://customurl.com';
// configFactory will use the endpointURL defined by this controller
$scope.fetchConfig = function(){
configFactory.fetchConfig().then(function(){}).catch(function(){});
}
})
// and another
.controller('someCtrl', function(configFactory){
configFactory.endpointURL = 'http://anotherurl.com
})
Ok, so I ended up with the following:
My ng-resources:
var ConfigurationResource = function ($resource, endpointUrl) {
var allowedMethods = {
'get': {
method: 'GET'
},
'update': {
method: 'PUT'
}
};
return $resource(endpointUrl, {}, allowedMethods);
};
var FacebookProperties = function ($resource) {
return ConfigurationResource($resource, 'my.facebook.endpoint.url');
};
And there was a problem with duplicating code in my controller, so it now looks like the following:
var PropertiesController = function (scope, propertiesResource) {
this.fetchProperties = function () {
propertiesResource.get(function (fetchedProperties) {
scope.properties = fetchedProperties;
});
};
this.updateProperties = function () {
propertiesResource.update(scope.properties)
};
scope.properties = {};
scope.fetchProperties = this.fetchProperties;
scope.updateProperties = this.updateProperties;
scope.fetchProperties();
};
var facebookConfigController = function ($scope, FacebookProperties) {
new PropertiesController($scope, FacebookProperties);
};
Maybe that's not the proper way to do this, but at least it works and there's almost no boilerplate.
My basic premise is I want to call back to the server to get the logged in user in case someone comes to the site and is still logged in. On the page I want to call this method. Since I am passing the user service to all my controllers I don't know which controller will be in use since I won't know what page they're landing on.
I have the following User Service
app.factory('userService', function ($window) {
var root = {};
root.get_current_user = function(http){
var config = {
params: {}
};
http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
Here is an empty controller I'm trying to inject the service into
app.controller('myResourcesController', function($scope, $http, userService) {
});
So on the top of my index file I want to have something along the lines of
controller.get_current_user();
This will be called from all the pages though so I'm not sure the syntax here. All examples I found related to calling a specific controller, and usually from within another controller. Perhaps this needs to go into my angularjs somewhere and not simply within a script tag on my index page.
You could run factory initialization in run method of your angular application.
https://docs.angularjs.org/guide/module#module-loading-dependencies
E.g.
app.run(['userService', function(userService) {
userService.get_current_user();
}]);
And userService factory should store authenticated user object internaly.
...
if (data.success == true) {
root.user = data.user;
}
...
Then you will be able to use your factory in any controller
app.controller('myController', ['userService', function(userService) {
//alert(userService.user);
}]);
You need to inject $http through the factory constructor function, for firsts
app.factory('userService', function ($window, $http) {
var root = {};
root.get_current_user = function(){
var config = {
params: {}
};
$http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
in your controller you can say
$scope.get_current_user = UserService.get_current_user();
ng attributes in your html if needed. besides this, i am not sure what you need.
I'm using $resource in angular to get json object and its structure is defined below
[
{
"id": 0,
"name": "Type1"
},
{
"id": 1,
"name": "Type 2"
}
]
after fetching the data .. console.log(jsonObject) gives me
[Resource, Resource, $promise: Object, $resolved: true]
How can I remove $promise & $resolved from the resulting object ?
I tried angular.fromJson(json) but still I see that these objects still exist.
I'm looking for the same answer, but at this moment I know only this ugly hack:
To write a func like this:
function cleanResponse(resp) {
return JSON.parse(angular.toJson(resp));
}
and call it in every single query (ugly, yep):
function getSmt() {
Resource.get({}, function (data) {
$scope.some = cleanResponse(data);
}, function (responce) {
//handle error
})
}
I'll be happy if someone would teach me how to do it correctly
Example from my project
Diary.getSharedWithMe(function(data) {
delete data.$promise;
delete data.$resolved;
_self.sharedDiariesWithMe = data;
}, function(error) {
console.log(error)
});
From this answer, it looks that yourResource.toJSON() is readily available.
Right, I've faced the same problem several times and always ended up using $http instead of $resource, however, today I decided to try and deal with this issue. I hope somebody will find this useful one day as well.
So, after you receive your object with $promise inside of it, what you do is just use angular.copy(data), like so:
someService.getSomething($scope.someId).$promise.then(function (data) {
if (data) {
$scope.dataWithoutPromise = angular.copy(data);
}
});
After that, you will see that your dataWithoutPromise just contains the objects that you need, and $promise and $resolved are gone.
I'm facing the same issue. In fact, you simply have to use the same version of angular and angular-resource and it will work like a charm.
Hope it helps !
Short answer : angular.fromJson(angular.toJson(resp));
This will remove all "angular specific" functions etc and will return you a clean data object.
Are you using isArray or .query() with your $resource object?
From the $resource docs: isArray – {boolean=} – If true then the returned object for this action is an array.
UPDATE:
If you are using $resource correctly, some options you have are:
1) Have the backend return the data contained within an object, eg. { data: [] } rather than just an array.
2) If that is not possible, use the callback from the $resource.query(), eg.
MyResource.query({ myParams: 'something'}, function(result) {
// Do something with the plain result
});
I did this quick reusable method for angular.toJson (Properties with leading $$ will be stripped):
yourApp.factory('Service', ['$resource', function ($resource) {
return $resource('your/rest/api/path');
}]);
yourApp.factory('commonServices', function () {
return {
toJson : function(response){
return JSON.parse(angular.toJson(response));
//*or*
//return angular.toJson(response);
}
});
And then something like this in your controller:
yourApp.controller('Controller', ['$scope', 'Service','commonServices',
function ($scope, Service, commonServices) {
Service.get().then(function (data) {
$scope.myData = commonServices.toJson(data);
});
});
the more simple way is to add angular.toJson in the controller
yourApp.controller('Controller', ['$scope', 'Service',
function ($scope, Service, commonServices) {
Service.get().then(function (data) {
$scope.myData = angular.toJson(data);
});
});
Please simply do this:
jsonObject.$promise = undefined;
jsonObject.$resolved = undefined;
$promise and $resolved will not be removed but you will be able to do what you want.
Try to code something like this in your service:
yourApp.factory('Service', ['$resource', function ($resource) {
return $resource('your/rest/api/path');
}]);
And then something like this in your controller:
yourApp.controller('Controller', ['$scope', 'Service', function ($scope, Service) {
Service.get().then(function (data) {
$scope.myData = data;
});
});