$ resource的自定义缓存

时间:2015-03-11 16:58:11

标签: angularjs caching angular-resource angular-http

我的AngularJS中有以下模式,它要求重构:

$scope.advertisers = Advertiser.query()
$scope.advertisersMap = {};
$scope.advertiser.$then(function (response) {
    arrayToMap(response.resource, $scope.advertisersMap)
}

arrayToMap是将数组中的每个项目添加到对象中并将其ID作为键的函数。

现在,我希望这会发生在Advertiser本身,例如

$scope.allAdvertisers = Advertiser.query()
// Somewhere else
var advertiser = Advertiser.get({id: 2})

Advertiser.get({id: 2})将从先前由查询方法填充的缓存返回。

Advertiser在工厂中定义:

.factory('Advertiser', ['djResource', 'Group', function ($djResource, Group) {

    var Advertiser = $djResource('/campaigns/advertisers/:advertiserId/', {advertiserId: '@id'});

    Advertiser.prototype.getGroups = function () {
        return Group.getByAdvertiser({advertiserId: this.id});
    };
    return Advertiser;
}])

可悲的是,DjangoRestResource(及其包装的$资源)通过网址缓存,因此query()将缓存 / advertisers / ,而get(2)将缓存 / advertisers / 2 / ,但我希望queryget能够检索它的方式进行缓存。

我试过通过执行缓存的包装函数替换query函数,但我希望它返回一个也像$ resource那样的数组的promise。它类似于:

    var oldQuery = Advertiser.query;
    var cache = $cacheFactory('advertisers');
    Advertiser.query = function () {
        var promise = oldQuery();
        return promise.then(function (response) {
            angular.forEach(response.resource, function (resource) {
                cache.put(resource.id, resource)
            })
        })
    };

但是返回的promise不再是类似于Array的对象,并且它不会像以前那样将返回的结果封装在Advertiser对象中,这会破坏我的大多数代码所需的{{1}最终将成为一个数组。

我应该尝试哪些其他方法?这个片段在多个工厂的每个控制器中重复出现,它会伤害我的眼睛。

2 个答案:

答案 0 :(得分:1)

以下是问题的解决方案:

function makeCachingMethod(object, method, cache, config) {
    var $q = angular.injector(['services']).get('$q');
    var oldMethod = object[method];
    object[method] = function () {
        var result;
        if (!config.isArray) {
            var id = config.idParam ? arguments[0][config.idParam] : arguments[0];
            result = cache.get(id);
            if (result !== undefined) {
                if (result.$promise === undefined) {
                    result.$promise = $q.when(result);
                    result.$resolved = true;
                }
                return result;
            }
        }
        result = oldMethod.apply(this, arguments);
        result.$promise.then(function (data) {
            if (config.isArray) {
                angular.forEach(data, function (item) {
                    cache.put(item.id, item);
                })
            } else {
                cache.put(data.id, data);
            }
            return data;
        });
        return result;
    }
}

一个示例用法:

app.factory('Country', ['$resource', '$cacheFactory', function ($resource, $cacheFactory) {
    var Country = $resource('/campaigns/countries/:id', {id: "@id"}, {
        query: {method: 'GET', url: '/campaigns/countries/', isArray: true},
        get: {method: 'GET', url: '/campaigns/countries/:id/', isArray: false}
    });
    var cache = $cacheFactory('countries');
    makeCachingMethod(Country, 'query', cache, {isArray: true});
    makeCachingMethod(Country, 'get', cache, {idParam: 'id'});
    return Country;
}])

这里发生了什么?

我使用makeCachingMethod来装饰由$resource创建的原始方法。遵循$resource本身使用的模式,我使用配置对象来表示装饰方法是否返回数组,以及如何在查询中传递id。不过,我假设要保存的ID的键是'id',这对我的模型是正确的,但可能需要更改。

注意到在从缓存中返回对象之前,装饰器会向其添加$promise$resolved属性,因为我的应用程序需要来自$resource的具有这些属性的对象,并且为了继续使用promises API,例如:

$scope.advertiser = Advertiser.get({advertiserId: $scope.advertiserId});
$scope.advertiser.$promise.then(function () {
    $scope.doSomething();
});

请注意,由于函数是在任何Angular模块的范围之外定义的,因此需要使用$q注入angular.injector()服务。一个更好的解决方案是返回一个用于调用装饰器函数的服务。这样的服务也可以自己处理缓存的生成。

此解决方案无法处理缓存模型的到期,这在我的方案中没有太大问题,因为这些很少会发生变化。

答案 1 :(得分:0)

我不知道直接使用$ resource支持任何缓存,您可以创建一个抽象广告商资源并自行处理缓存的工厂。

以下是有关缓存的一些有用信息: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#validating-cached-responses-with-etags

我正在使用$ resource处理应用程序,并且每个方法都是抽象的,所以我可以检查缓存以查看我请求的是否存在并返回它,如果没有,则使用$ resource进行调用