$resource 的自定义缓存

Custom caching for $resource

我的 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}) 将 return 来自查询方法先前填充的缓存。

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(以及它包装的 $resource)通过 URL 进行缓存,因此 query() 将缓存 /advertisers/get(2) 将缓存 /advertisers/2/,但我希望 queryget 也能够检索它的方式进行缓存。

我已经尝试用执行缓存的包装函数替换 query 函数,但我希望它成为 return 一个 promise,它也是一个像 $resource 一样的数组。它是这样的:

    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)
            })
        })
    };

但是 returned promise 不再是类似数组的对象,并且它不会像以前那样将 returned 结果封装在 Advertiser 对象中,这会中断我的大部分代码都希望 Advertiser.query() 最终成为一个数组。

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

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

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

我正在使用 $resource 开发一个应用程序,每个方法都是抽象的,所以我可以检查缓存以查看我请求的内容是否存在,return 如果没有,使用 $resource.

拨打电话

问题的解决方法如下:

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 本身使用的模式,我使用一个配置对象来指示装饰方法 return 是否是一个数组,以及如何在查询中传递 id。不过,我假设要保存的 ID 的密钥是 'id',这对我的模型来说是正确的,但可能需要更改。

注意到在 return 从缓存中获取对象之前,装饰器向其添加了 $promise$resolved 属性,因为我的应用程序期望对象来自 $resource具有这些属性,并且为了继续使用承诺 API,例如:

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

请注意,由于该函数是在任何 Angular 模块的范围之外定义的,因此需要使用 angular.injector() 注入 $q 服务。更好的解决方案是 return 调用装饰函数的服务。这种服务还可以处理缓存本身的生成。

此解决方案不处理缓存模型的过期问题,这在我的场景中不是什么大问题,因为它们很少改变。