I have a simple User $resource that uses the default $http cache implementation like so:
factory('User', function($resource){
return $resource(endpoint + '/user/current/:projectId', {},
{get:
{
cache: true,
method: 'GET'
}
}
);
})
This works very well, i.e. my server is only called once in my application, then the value is fetched from cache.
But I need to refresh the value from the server after a certain operation. Is there an easy way to do that?
Thanks.
Keep the boolean and get the $http cache:
var $httpDefaultCache = $cacheFactory.get('$http');
Then you can control it like any another cache made with $cacheFactory, a usage instance provided below:
$httpDefaultCache.remove(key);
// Where key is the relative URL of your resource (eg: /api/user/current/51a9020d91799f1e9b8db12f)
Instead of using a boolean argument in the cache property of each action you can pass on a cache instance created with $cacheFactory which you can have more control over (i.e. clear the cache).
Example usage:
app.factory('Todos', function($resource, $cacheFactory) {
var cache = $cacheFactory('todo');
return $resource(apiBaseUrl + '/todos/:id', { id: '#id' }, {
'get': { method: 'GET', cache: cache },
'query': { method: 'GET', cache: cache, isArray: true }
});
});
I came across this thread looking for something similar, but found that $resource will manage the cache for you automatically, so there's no need to force the cache to be cleared.
The idea is that if you have a resource that you can query, that query response will be cached, but if you save something for that same resource, the previously cached data must be invalid, so it is cleared for you. It makes sense that it would work this way.
Here's some code I use to do this (you can ignore the possibly odd-looking factory creation part and pay attention to the "class" body).
'use strict';
sampleApp.players.$ng.factory('sampleApp.players.PlayerService', [
'$log',
'$resource',
sampleApp.players.PlayerService = function ($log, $resource) {
var service = {};
$log.info('Creating player resource.');
var Player = $resource('/api/players', {}, {query: {
isArray: true,
cache: true,
method: 'GET'
}});
service.addPlayer = function(playerName) {
$log.info('Saving a new player.');
return new Player({name: playerName}).$save();
};
service.listPlayers = function () {
$log.info('Fetching players.');
return Player.query();
};
return service;
}]);
If you call the listPlayers function several times, the first call makes a http get request and all subsequent calls are cached. If you call addPlayer though, a http post is performed as expected, and then the next call to listPlayers will perform a http get (not cached).
This keeps you out of the business of managing someone else's ($http) cache and trying to keep up with which url's are being used for requests and which are clearing caches at the right times.
I suppose the moral of the story here is to work with the library and all will be well... except for any bugs or incomplete features, but Angular doesn't have any of those ;)
p.s. This is all running on AngularJS 1.2.0.
Related
I have 4 controllers in a page in AngularJS. Each controller calls Api's via http request(scope $http). I want to ensure that all the Api's has been called and loaded till then I can show the loading gif image. How to check all the Api's has been loaded in the page in AngulaJS.
I am not sharing the exact code some variable and name I have modified.
myApp.controller('testController',function ($scope, $http, $q, $filter) {
var _promises = {};
_promises['abc'] =
$http({
url: API_URL+'abc-type/',
method: 'GET',
params: {'test1': 'test2'}
});
_promises['abc1'] =
$http({
url: API_URL+'abc-type2/',
method: 'GET'
});
}
$q.all(_promises).then(function (res) {
alert("All promises executed.");
});
});
$http uses promises ($q) for it's API. You can use the $q.all method to run a callback when an array of $http requests are resolved (you will need to make sure all service requests return promises to avoid undefined behavior).
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
It would look something like this
$scope.showLoadingGif = true;
$q.all([MyService.makeGet(), MyService.makeAnotherGet(), ...]).then(function(responses) {
// all the calls have returned a response by this point
$scope.showLoadingGif = false;
})
I'm having to inline all resources into one file and this includes all the data that my application uses. With a gulp process, I've been able to create a $cacheFactory with all the data:
angular.module('app').run('$cacheFactory', '$http', function($cacheFactory, $http){
var $httpDefaultCache = $cacheFactory.get('$http');
$httpDefaultCache.put('/data/names.json',{...});
$httpDefaultCache.put('/data/places.json',{...});
});
My understanding of how to access this instead of making a call externally (file) may be incorrect.
I thought that by setting $httpProvider.defaults.cache = true, that my request to the endpoints of above would use the default cache.
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.cache = true;
}]);
Instead I get an error of
https://.../data/names.json 404 (Not Found)
As if it is looking within the the client and not in angular's cache. The type is also an xhr.
data.load(names, '/data/names.json');
...
function load(broadcastName, url, force){
if(!isLoaded[broadcastName] && !force){
console.log('Loading name data from', url);
// Make the http GET request
http({
method: 'GET',
url: url,
cache: true
}).then(function success(response){
console.log("response: ", response)
...
})
Had to create a custom http request that perfectly imitates angular $http service. It is used in several other applications we have, so I know it works. Only thing that has been added for this implenation is cache:true.
I've looked at several other questions that were similar, but I am still not understanding how it is working. How does using http default cache work? And is there something I should be aware of that I may be glossing over?
I hope this makes sense.
There's no special term for $http cache like '$httpDefaultCache'. It works like you expect it to work. You set either $httpProvider.defaults.cache or cache request option to true, and the response is retrieved from $http cache, which is available by default as $cacheFactory.get('$http').
Cache is just key/value storage. If request URL doesn't match a key completely, cache won't be used.
Here's an example how it works:
$cacheFactory.get('$http').put('/foo', 'foo');
$http({ url: '/foo', cache: true })
.then(result => {
$scope.foo = result.data;
})
.catch(result => console.error(result))
As the title states, I'm using a $resource object to do a GET request.
Can someone who has in-depth knowledge explain the difference when setting
cache: true
query: {
method: 'GET',
cache: true
}
VS having something like:
var theCache = $cacheFactory('myCache');
and using that cache object on the resource by doing this:
query: {
method: 'GET',
cache: theCache
}
By passing the true parameter, you're telling angular to use the default (global) $http cache, which means the response will be cached and every next request made through $http or $resource (which is a high-level abstraction over the $http) will be served from cache (no extra requests to the server). The key for this request in the $http cache is the full-path URL. You can obtain the default $http cache instance using the $cacheFactory:
var cache = $cacheFactory.get('$http');
Now, if you want to create a resource-specific cache you create it using the $cacheFactory and tell the $http or $resource to use it:
var myCache = $cacheFactory.get('myCache');
$http.get('/api/users.json', {
cache: myCache
});
// or
$resource('/api/users.json', null, {
query: { cache: myCache }
});
This way no other requests will be sharing this cache anymore. Additionally, you can specify the capacity of this cache while instantiating it, or clear it whenever you need to without affecting the other service.
I have the following example method in angular service:
function send(data) {
return $http({
method: 'POST',
url: 'https://test.domain/test/send',
data: $httpParamSerializerJQLike(data)
});
}
The domain that is https://test.domain/test is the same for all the services in my app. I do not want to write it every time in every services. I can abstract it in a constant and inject it in every service but I wonder if there is more clever solution. Is it possible to store the domain part in an interceptor or any other suggestions are welcome. Please provide code examples as I am quite new to angular. Thanks
I'd say rather than abstracting the values out into a constant, you should abstract the $http call into a service. Then you can just inject that service into all of your other services in place of $http. For example:
angular.module('myApp').factory("myHttp", ["$http", function ($http) {
return function (config) {
config.url = "https://test.domain/test" + config.url;
return $http(config);
};
}]);
So effectively what this service is doing is proxying calls to $http, but prepending your common URL to the beginning - this would allow you to change your example code to:
function send(data) {
return myHttp({
method: 'POST',
url: '/send',
data: $httpParamSerializerJQLike(data)
});
}
Of course, this is just one example of how you could do an abstraction like this - the myHttp service could take any form you like, depending on what would be most convenient for you. I think this is a better solution than using an interceptor in this case, as it allows you to pick and choose when you use it, rather than it being applied to every single HTTP request.
create an interceptor and on requests change the url.
angular.module('app').factory('domainInterceptorService', [
function () {
var request = function (config) {
config.url = 'https://test.domain/' + config.url;
}
return config;
}
return {request: request};
});
I find myself frequently using this construct in my controllers:
$http({method: 'GET', url: '../ajax/mycallback', cache: false}).
success(function(data) {
$scope.data = data;
});
Is there any way to make this more concise? In particular, is there a kind of "default success method" that just stores the result on the scope? Also, is there a way to globally set cache to false, so I can use $http.get() ?
Option 1: you only need to pass the value to $scope
$http returns a promise, so you can share the returning result directly with the scope:
$scope.data = $http({method: 'GET', url: '../ajax/mycallback', cache: false})
Angular has automatic promise fulfillment support in templates. So if you assign the data to templates, it will automatically trigger the digest cycle upon fulfillment (read: it will trigger the update when the $http will succeed).
But in this case I would suggest you to use $resource instead of $http and incapsulate the $resource creation as a service.
You also can use $resource the same way, as it has natural automatic promise fulfillment. Natural states that promises will be replaced with actual values (this logic includes arrays support!):
$scope.data = MyResourceService(...)
Option 2: you have a more complex callback
You can simply create a factory function to create a closure within a specific scope:
function cb(scope, scopeObjName) {
return function(data){
scope["scopeObjName"] = data;
// common logic goes here
}
}
and use it like this:
$http({method: 'GET', url: '../ajax/mycallback', cache: false})
.success(cb($scope, "data"))
As you can see, the answer depends on the actual usecase and can be improved further.
You could do the following:
myApp.config(function($httpProvider){
$httpProvider.defaults.cache = false;
});
as for your callback, If that is all you're doing, the anonymous function is the best option. Otherwise you'd have to bind a generic function every http invocation and pass it a context..like this:
$http.get(...).success(standardSuccess.bind({$scope:$scope,p:'data'}));
function standardSuccess(data){
this.$scope[this.p] = data;
}
Then you would just keep that in an injectable somewhere. But as you can see is more verbose and less readable. And slower.
Another option is to pass the scope into the httpConfig itself. and use an interceptor to set your property. (as the config is received in an interceptor. Roughly like:
myApp.config(function($httpProvider){
$httpProvider.defaults.cache = false;
$httpProvider.interceptors.push('defaultHttpSetter');
});
myApp.factory('defaultHttpSetter',function($q){
return {
'response': function(response){
response.config.scope[response.config.prop] = response.data;
return $q.when(response);
}
};
});
Which would be used like..
$http.get('/myurl',{scope:$scope,prop:'data'});
I don't have issue with the other answers, but just in case they are over-thinking it. You can do this:
var genericSuccessFunction = function(data) {
$scope.data = data;
}
You can create your result function as part of the controller and save it as a controller local variable.
Then you just need to pass the result function into the success:
$http({method: 'GET', url: '../ajax/mycallback', cache: false}).success(genericSuccessFunction);
In my experience, I often find myself using this approach for an error function, but I don't often find myself able to use generic success functions.