angularjs: how to add caching to resource object?

To add caching inside http is pretty straight forward. ( by passing cache=true )

http://docs.angularjs.org/api/ng.$http has Cache option.

How do I add similar functionality in $resource in angularjs ?

Implementing your own cache in AngularJs is quite easy. Just use $cacheFactory:

app.factory('myService', function($resource, $cacheFactory) {
   var cache = $cacheFactory('myService');
   var User = $resource('/user/:userId', {userId:'@id'});

   return {
      getResource: function(userId) {
         var user = cache.get(userId);
         if (!user) {
            user = User.get({userId:userId});
            cache.put(userId, user);   
         }
         return user;
      }
   };
});

Since 1.1.2 (commit), all the $httpConfig options are directly exposed in $resource action objects:

return {
  Things: $resource('url/to/:thing', {}, {
    list : {
      method : 'GET',
      cache : true
    }
  })
 };

As the docs state, $resource has built-in support for $cacheFactory. You can pass it in via the cache property of each action:

cache{boolean|Cache} – If true, a default $http cache will be used to cache the GET request, otherwise if a cache instance built with $cacheFactory, this cache will be used for caching.

Example usage:

app.factory('Todos', function($resource, $cacheFactory) {
    var todosCache = $cacheFactory('Todos');
    return $resource(apiBaseUrl + '/todos/:id', {id: '@id'}, {
        'get': { method:'GET', cache: todosCache},
        'query': { method:'GET', cache: todosCache, isArray:true }
    });
});

This doesn't seem to be mentioned here but you can also overwrite the default methods.

app.factory("List", ["$resource", function($resource) {
    return $resource("./lists/:path/:action.json", {}, {
        get: {
            method: "GET",
            cache: true
        }
    });
}]);

You can also set default cache for $http and thus for $resource which is based on it.

My settings with the excellent angular-cache allowing LocalStorage and compliant with $cacheFactory:

app.run(function($http, DSCacheFactory) {

    DSCacheFactory('defaultCache', {
        deleteOnExpire: 'aggressive',
        storageMode: 'localStorage' 
    });

    $http.defaults.cache = DSCacheFactory.get('defaultCache');
});

Looking at the angular-resource source indicates that triggering caching isn't possible with the way it is currently written.

Here's the request object from the source:

$http({
    method: action.method,
    url: route.url(extend({}, extractParams(data), action.params || {}, params)),
    data: data
 }).then(...)

There are a few potential ways to deal with this.

First, you could cache locally using client-side persistence. I use amplify.store with wrapper (b/c I don't really like the API syntax). There are a variety other storage solutions depending on what you're looking for and what browser's your targeting. Quite a few people use lawnchair as well.

You can then stringify and store your models locally and update them based on whatever rules or time limits you desire.

Another solution is to simply modify angular resource to accept the parameters you're looking for. This could be as simple (simply add an additional argument to $resource) or complex as you need it to be.

e.g.

function ResourceFactory(url, paramDefaults, actions, cache) {
   ...
   var cache = cache != null ? cache : false; // Set default to false
   $http({
        method: action.method,
        url: route.url(extend({}, extractParams(data), action.params || {}, params)),
        data: data,
        cache: cache
    }).then(...)       
}

Finally, depending on you requirements, it might be significantly easier to simply create your own resource, using angular.factory to create a service. One of the advantages of ngResource is that is does all of the string interpolation work for you when translating parameters. However, you can certainly barrow this logic for parsing if you need it, or write your own based on the models you're using.

I just came across this really well thought out module called angular-cached-resource that will do the job for you. https://github.com/goodeggs/angular-cached-resource

It is a drop in replacement for $resource, with added functionality of cache management using localStorage. If your browser doesnt support local storage, you will not get any caching benefit. Here's an example of how you can use it:

The old way using $resource:

var Entry = $resource('/entries/:slug', {slug: '@slug'});

var announcement = new Entry();
announcement.slug = 'announcing-angular-cached-resource';
announcement.title = 'Announcing Angular Cached Resource';
announcement.body = 'Here is why Angular Cached Resource is awesome!';

announcement.$save(function() {
  alert('Saved announcement.');
});

The new way using $cachedResource:

var Entry = $cachedResource('entries', '/entries/:slug', {slug: '@slug'});

var announcement = new Entry();
announcement.slug = 'announcing-angular-cached-resource';
announcement.title = 'Announcing Angular Cached Resource';
announcement.body = 'Here is why Angular Cached Resource is awesome!';

announcement.$save(function() {
  alert('Saved announcement.');
});

The only differences in the code are:

  1. Use $cachedResource instead of $resource
  2. Provide a "key" (entriesin the example above) so that you can refer to it even between page refreshes or reloads. These entries persist since out of the box it uses localStorage.

A detailed tutorial is available here: https://github.com/goodeggs/bites/blob/master/src/documents/open_source/2014-04-24-angular-cached-resource.md

Also note Angular 2.0 may support something like this out of the box: https://docs.google.com/document/d/1DMacL7iwjSMPP0ytZfugpU4v0PWUK0BT6lhyaVEmlBQ/edit