Patrick
Patrick

Reputation: 13974

cache and set scope value in angular.js

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

Answers (1)

jszobody
jszobody

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

Related Questions