我的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 / ,但我希望query
以get
能够检索它的方式进行缓存。
我试过通过执行缓存的包装函数替换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}最终将成为一个数组。
我应该尝试哪些其他方法?这个片段在多个工厂的每个控制器中重复出现,它会伤害我的眼睛。
答案 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进行调用