I'm looking at Angular's generic factory for CRUD (which I am currently in preference of over using a service):
app.factory('dataFactory', ['$http', function ($http) {
var urlBase = '/odata/ContentTypes';
// The _object_ defined in the factory is returned to the caller, rather than as with a service,
// where the _function_ defined in the service is returned to the caller
var dataFactory = {};
dataFactory.getContentTypes = function () {
var contentTypes = $http.get(urlBase);
return contentTypes;
};
dataFactory.getContentType = function (id) {
return $http.get(urlBase + '/' + id);
};
dataFactory.insertContentType = function (contentType) {
return $http.post(urlBase, contentType);
};
dataFactory.updateContentType = function (contentType) {
return $http.put(urlBase + '/' + contentType.ID, contentType);
}
dataFactory.deleteContentType = function (id) {
return $http.delete(urlBase + '/' + id);
}
// to traverse navigation properties
//dataFactory.getUserFromContentTypeId = function (id) {
// return $http.get(urlBase + '/' + id + '/user');
//}
//dataFactory.getOrders = function (id) {
// return $http.get(urlBase + '/' + id + '/orders');
//};
return dataFactory;
}]);
For all my entities, this code will be roughly the same. Is it possible to inject the entity name (or corresponding RESTful path), and if so, can this be treated like a partial class, where if additional promises are required (such as traversing navigation properties), they can also be injected?
If this is possible to do with Angular, can someone post some examples?
Personally I like to keep my code DRY and I've found that if you keep a common convention on your server API you can go a very long way with a base factory/repository/class or whatever you want to call it. The way I achieve this in AngularJs is to use a AngularJs factory that returns a base repository class, i.e. the factory returns a javascript class function with prototype definitions and not an object instance, I call it abstractRepository
. Then for each resource I create a concrete repository for that specific resource that prototypically inherits from abstractRepository
, so I inherit all the shared/base features from abstractRepository
and define any resource specific features to the concrete repository.
I think an example will be clearer. Lets assume your server API uses the following URL convention (I'm not a REST purest, so we'll leave the convention up to whatever you want to implement):
GET -> /{resource}?listQueryString // Return resource list
GET -> /{resource}/{id} // Return single resource
GET -> /{resource}/{id}/{resource}view // Return display representation of resource
PUT -> /{resource}/{id} // Update existing resource
POST -> /{resource}/ // Create new resource
etc.
I personally use Restangular so the following example is based on it, but you should be able to easily adapt this to $http
or $resource
.
abstractRepository
app.factory('abstractRepository', [function () {
function abstractRepository(restangular, route) {
this.restangular = restangular;
this.route = route;
}
abstractRepository.prototype = {
getList: function (params) {
return this.restangular.all(this.route).getList(params);
},
get: function (id) {
return this.restangular.one(this.route, id).get();
},
getView: function (id) {
return this.restangular.one(this.route, id).one(this.route + 'view').get();
},
update: function (updatedResource) {
return updatedResource.put();
},
create: function (newResource) {
return this.restangular.all(this.route).post(newResource);
}
// etc.
};
abstractRepository.extend = function (repository) {
repository.prototype = Object.create(abstractRepository.prototype);
repository.prototype.constructor = repository;
};
return abstractRepository;
}]);
Concrete repository, let's use customer as an example:
app.factory('customerRepository', ['Restangular', 'abstractRepository', function (restangular, abstractRepository) {
function customerRepository() {
abstractRepository.call(this, restangular, 'customers');
}
abstractRepository.extend(customerRepository);
return new customerRepository();
}]);
And then in your controller you inject customerRepository
:
app.controller('CustomerController', ['$scope', 'customerRepository', function($scope, customerRepository) {
// Use your customerRepository
}]);
What you'll find if you use this base repository pattern is that most of your CRUD controllers will also share a lot of common code, so I typically create a base CRUD controller that my controllers inherit from using $injector.invoke
, but I'll leave examples of this out as it is out of scope of this question.