It is possible to have a very generic base $resource and inherit it.
I found the answer on a SO post that i can't found anymore. If someone found it edit my post to add it.
Here is the code that I use :
angular.baseResourceServiceMaker = function(service){
return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint', 'Utils',
function($injector, $resource,TypeService, $http, _, BackEndpoint, Utils){
this.restUrl = BackEndpoint+'/rest/';
this.baseName = '';
this.resource = null;
// from angular-resource
var toString= function() {
var value = [];
_.forEach(this, function(e) {
value.push('' + e);
});
return '[' + value.join(', ') + ']';
};
var isObject = function isObject(value) {
// http://jsperf.com/isobject4
return value !== null && typeof value === 'object';
};
var isFile = function(obj) {
return toString.call(obj) === '[object File]';
}
var isFormData = function(obj) {
return toString.call(obj) === '[object FormData]';
}
var isBlob = function(obj) {
return toString.call(obj) === '[object Blob]';
}
this.defaultToJson = function(d) {
return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? angular.toJson(d) : d;
};
this.typeServiceProcessData = function(d){
return TypeService.processData(d);
};
this.typeServiceProcessJsData = function(d){
return TypeService.processJsData(d);
};
this.generateTransformRequestFn = function(mapKeyValues){
return function(data){
var object = {};
_.forEach(_.keys(mapKeyValues), function(key){
Utils.setAttributeValue(object, key, Utils.getAttributeValue(data, mapKeyValues[key]));
});
return object;
}
};
this.addedMethods = {};
// use of resource will be internal, to handle transformation of data
// and so on...
this.getResource = function(){
if(this.resource == null){
var baseResourceUrl = this.restUrl + this.baseName + '/';
var baseResourceMethods = {
'get': {method:'GET', transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+':id'},
'create': {method:'POST', url:baseResourceUrl, transformRequest:[this.typeServiceProcessJsData, this.defaultToJson]},
'update' : {method:'PUT', transformRequest:[this.typeServiceProcessJsData, this.defaultToJson]},
'search': {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+'search/(:search)/:offset/:limit/:order',
params: {offset:0, limit:50, order:"creationDate=asc"}
},
'custom_search': {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+':prefix/search/(:search)/:offset/:limit/:order',
params: {search:'pk=gt=0',offset:0, limit:50, order:"creationDate=asc"}
},
'list': {method:'GET', isArray:true, transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+'search/(pk=gt=0)/0/50/creationDate=asc'
},
'delete': {method:'DELETE'}
};
_.forEach(_.keys(this.addedMethods), function(key){
baseResourceMethods[key] = this.addedMethods[key];
}, this)
this.resource = $resource(baseResourceUrl+':id',
{id:'@pk'}, baseResourceMethods
);
}
return this.resource;
};
this.get = function(id){
this.getResource().get({id:id});
};
this.create = function(data){
this.getResource().create(data);
};
this.update = function(data){
this.getResource().update(data);
};
this.search = function(searchQuery){
this.getResource().search({search:searchQuery});
};
this.searchPaginate = function(searchQuery, offset, limit){
this.getResource().search({search:searchQuery, offset:offset, limit:limit});
};
this['delete'] = function(id){
this.getResource()['delete']({id:id});
};
// Finishes the other injections
$injector.invoke(service, this);
}];
};
Some comments about this code :
- The functions isFile/isObject,... are c/c from angular.js because I keep the defaultTransformResponse from angularJS, this function use internal function that are not in my scope so I had to cc it.
- I define default methods create/update/...
- I have a typeService where i declare all my type and fields, so i can automatically convert type : for instance my server's date are always timestamps, so i convret it automatically to Javascript Date in transformResponse and the opposite in transformRequest.
- addedMethods is used to add others method.
- restUrl is the entry point of all rest services, baseName must be set by the implementation, it's the entry point for the resource. BackEndPoint is the constant that define the contextPath of my application.
Example of usage :
.service('ArticleService',angular.baseResourceServiceMaker(['$http', function($http){
this.baseName = 'article';
var baseResourceUrl = this.restUrl + this.baseName + '/';
this.addedMethods.root ={
method:'GET', isArray:true,
transformResponse:[$http.defaults.transformResponse[0], this.typeServiceProcessData],
url:baseResourceUrl+'root'
};
}]))