AngularJS - How to cache service`s ajax result to reuse in controller - javascript

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
}]);

Related

Angular1.6 Factory

Hii I m using following code. I am reading a json file name is "users.json". If i read this file in controller through $http everything works fine. but i want to use the data that i read from file, again and again in different controller so i made a factory for this. but in factory when i read data from that json file through $http.get() and in return when i call that service method in my controller and it returns Object { $$state: Object }
app.factory('AboutFactory',['$http',function ($http) {
return {
getter: function () {
return $http({
method : 'GET',
url : '/home/penguin/Modeles/users.json',
cache : true
})
.then(function (response) {
return response.data
})
}
}
}])
Result of getter function is a promise. so you should use it like this:
AboutFactory.getter().then(function(res)
{
console.log(res);
});
That's because the $http service returns a promise as mentioned in the documentation:
The $http API is based on the deferred/promise APIs exposed by the $q
service. While for simple usage patterns this doesn't matter much, for
advanced usage it is important to familiarize yourself with these APIs
and the guarantees they provide.
You can think of a promise as if you give a top secret message to someone to deliver personally to a friend, then when delivered, report back to you with a message back from your friend.
You provide the message (the request object) to the person so that they can attempt to make the delivery of the message (send the request).
The attempted delivery has taken place (the request has been sent), it either:
a) was delivered successfully (successful response) or
b) your friend was not in so the letter could not be delivered (non success response).
You can then act depending on the response you get back
a) Message was delivered (it was a successful request) and you got a letter back (do something with the response) or
b) Message failed to get delivered (request wasn't successful), so you can maybe try again later or do something else as you don't have the information you requested
Here is an example of using the $http service with the $q service:
// app.js
(function() {
'use strict';
angular.module('app', []);
})();
// main.controller.js
(function() {
'use strict';
angular.module('app').controller('MainController', MainController);
MainController.$inject = ['AboutFactory'];
function MainController(AboutFactory) {
var vm = this;
AboutFactory.getter().then(function(data) {
// do something with your data
vm.data = data;
}, function(error) {
// give the user feedback on the error
});
}
})();
// about.service.js
(function() {
'use strict';
angular.module('app').factory('AboutFactory', AboutFactory);
AboutFactory.$inject = ['$http', '$q']
function AboutFactory($http, $q) {
var service = {
getter: getter
};
return service;
function getter() {
// perform some asynchronous operation, resolve or reject the promise when appropriate.
return $q(function(resolve, reject) {
$http({
method: 'GET',
url: 'https://httpbin.org/get',
cache: true
}).then(function(response) {
// successful status code
// resolve the data from the response
return resolve(response.data);
}, function(error) {
// error
// handle the error somehow
// reject with the error
return reject(error);
});
});
}
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController as MainCtrl">
<pre>{{MainCtrl.data | json}}</pre>
</div>
Try this approach. It will work as per your expectation.
Read JSON file in controller through $http service as it is works fine.
For sharing the response data from one controller to another you can create a service and store the data into that service.
Service :
app.service('setGetData', function() {
var data = '';
getData: function() { return data; },
setData: function(requestData) { data = requestData; }
});
Controllers :
app.controller('myCtrl1', ['setGetData',function(setGetData) {
// To set the data from the one controller
var data = 'Hello World !!';
setGetData.setData(data);
}]);
app.controller('myCtrl2', ['setGetData',function(setGetData) {
// To get the data from the another controller
var res = setGetData.getData();
console.log(res); // Hello World !!
}]);
Here, we can see that myCtrl1 is used for setting the data and myCtrl2 is used for getting the data. So, we can share the data from one controller to another controller like this.

Send POST request and get items in Angular

Im writing my first app with Angular and now faced up with the problem... I have address for POST request with authentication token. Something like:
http://example.com/orders?authentication_token=123456
So I need to make ng-submit or ng-click that send that request and get a bunch of items and show them on the page...
Also, I have a body for them:
{
"order": {
"seller_id":84,
"price":123,
"delivary_date":"12-12-2025",
}
}
So, what the best way to do that?
So you will have to make one angular service which would communicate with server and fetch the data and one angular controller which will interact with service to get the data and display over the UI.
Lets say service name is MyService:
app.service('MyService', function($http) {
var params = {}; // some parameters
this.getData = function(successCallback, failureCallback) {
$http.post("URL", params).then(function(data) {
successCallback(data);
}, function(data, status) {
failureCallback(data, status);
});
}
});
Controller name is MyCntrl:
app.controller('MyCntrl', function($scope, MyService) {
function successCallback(data) {
$scope.itemList = data;
}
function failureCallback(data, status) {
$scope.itemList = {};
}
$scope.handleClick = function() {
MyService.getData(successCallback, failureCallback);
}
});
I believe it would help you to resolve your requirement!!!
Assume you have a orderCtrl. ng-click or ng-submit is based on your app requirement. Call the function someFunction() that triggers $http post and you can handle the success and failure response.
app.controller('orderCtrl', function($scope, $http) {
$scope.someFunction = function(){
var data = {}; // prepare your data here.
$http({
method : "POST",
url : "specify your url here",
data : data
}).then(function mySucces(response) {
var response = response.data;
// handle your success here
}, function myError(response) {
// handle the failure here
});
});
});
Note :
If you are using a form and you want to trigger this function after user filling all the information, then use ng-submit. If it is independent then use ng-click.
I'm saying again, it's all depends on what you are doing.

AngularJS - Am I missing something in this Factory injection?

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.

Few angular services, differing only in constants

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.

AngularJS call common controller function from outside controller

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.

Categories

Resources