Reputation: 13974
I have a multi tabbed application, with two separate controllers.
When either tab is entered, I need to hit an API. The response doesn't update after the initial hit, so there is no need to hit it again on subsequent visits to that tab.
My question is what is the correct way to cache the API response, and set it to a scope variable.
Currently, I have a helped function setup like this
var setAndCache = function(scope, cacheFactory, cacheKey, cacheValue) {
scope[cacheKey] = cacheValue;
cacheFactory.put(cacheKey, cacheValue);
};
A cache factory setup like so
factory('TabData', function($cacheFactory) {
return $cacheFactory('tabData');
}).
that gets injected into each controller
controller('TabOne', function($scope, $http, TabData) {
var setupCache = function(response) {
setAndCache($scope, TabData, 'tabOneData', response);
};
if (!TabData.get('tabOneData')) {
$http.get('/path/to/api')
.success(function(response) {
setupCache(response);
});
}
else {
setupCache(TabData.get('tabOneData'));
}
This works fine, but feels...dirty. Is there a better way to achieve the same thing?
Upvotes: 4
Views: 5406
Reputation: 28939
I've been working through resource caching myself. Here is how I'm doing it so far.
I start with a cacheManager service:
app.factory('cacheManager', function($cacheFactory) {
var cache = $cacheFactory('resource');
return {
/**
* This will handle caching individual resource records
* @param CacheId string where we expect this to be stored in the cache
* @param Resource resource The resource object that we want to get
* @param Object param An object of params to pass to resource.get
* @param Function callback
* @return resource object
*/
fetchResource: function(cacheId, resource, params, callback) {
var result = cache.get(cacheId);
// See if we had a valid record from cache
if(result) {
console.log("cache hit: " + cacheId);
callback(result);
return result;
} else {
console.log("cache miss: " + cacheId);
result = resource.get(params, function(response) {
if(response.error) {
// We don't have a valid resource, just execute the callback
callback(response);
return false;
}
console.log("putting resource in cache");
cache.put(cacheId, response);
callback(response);
});
return result;
}
},
<snip update/delete methods, etc>
Then in my controller, I inject both the cacheManager service and my Project resource (for example), and then can do:
$scope.data = cacheManager.fetchResource('project.' + $scope.id, Project, {id: $scope.id}, function(response) {
...
});
I like how clean this keeps my controllers.
I know in your case you are using $http directly rather than a resource, but the same approach could be used. I'd personally recommend abstracting as much of the logic out into a cache wrapper service as possible, and minimize the overhead in each of your controllers.
Update:
As mentioned below in a comment, there is a much simpler take on caching of resources that is worth looking at. I had originally started with this example and then built it into what I'm now using above.
angularjs: how to add caching to resource object?
Upvotes: 3