我有几个简单的REST资源(人员,房屋等),通常实现为:
angular
.module('People')
.factory('Person', ['$resource', function($resource) {
return $resource('/api/people/:id', {id: '@id'}
);
}]);
每种类型的资源都有一个View页面和一个Edit页面。但是控制器中每种类型资源的功能大致相同。例如,View控制器总是按如下方式实现:
angular
.module('People')
.controller('PersonCtrl', ['$scope', '$routeParams', '$location', 'Person', function ($scope, $routeParams, $location, Person) {
// load the person
$scope.person = Person.get({id: $routeParams.id});
// edit this person
$scope.edit = function() {
$location.path($location.path() + '/edit');
};
}]);
编辑控制器总是这样:
angular
.module('People')
.controller('PersonEditCtrl', ['$scope', '$routeParams', '$location', 'Person',
function($scope, $routeParams, $location, Person) {
// edit mode!
$scope.editMode = true;
// get the person
$scope.person = Person.get({id: $routeParams.id});
// delete this person
$scope.delete = function() {
if (window.confirm('Are you sure you wish to delete ' + $scope.person.name + '?')) {
Person.delete({id: $routeParams.id}, function() {
$location.path('/people');
});
}
};
$scope.save = function() {
Person.update($scope.person);
$location.path('/people/' + $routeParams.id);
};
}
]);
干扰控制器的最佳方法是什么,以便我可以为任意资源重用相同的代码?
答案 0 :(得分:1)
我建议为控制器创建一个模块以及控制器所需的任何其他资源。然后将该模块注入您的顶级模块。
所以:
angular.module('Edit').
controller('EditController', ['$scope', function($scope) {
...
}]);
angular.module('People', ['Edit']);
angular.module('Houses', ['Edit']);
编辑: 事实证明它并不那么简单,对吧?正如您在评论中指出的那样,您还需要包含一些正在编辑的数据的表示。这可能会变得棘手,因为您正在尝试使用可用于编辑不同类型数据的统一控制器。它们必须具有统一的界面才能使其正常工作。
免责声明:我绝不能声称这是 Angular Best Practice ,但这是我实施的内容。在实践中,需要一些肮脏的黑客,但我认为更仔细的数据结构设计可以缓解这一点。下面是我的实现的简化示例,带有注释。此控制器的目的是在列表中显示不同类型的数据。
var listModule = angular.module('list', [/*dependencies*/]).
// data in my case is the result of the route resolve.
// if you don't use routing, then you can probably just store the data in the service
controller('ListController', ['$scope', 'service', 'data',
function($scope, service, data) {
// whatever your controller needs to do - such as:
$scope.save = function() {
service.save($scope.someData).
then(function(r) {
// update the UI
});
};
}
]).
// this is the "interface" that your services will implement
// definitely not something you will find in the angular docs, but basic OOP, right?
// since it's a provider you can make it even more reusable with a configuration api
provider('Listable', [
function() {
return {
// optional configuration methods
setBaseUrl: function(url) {
this.baseUrl = url;
},
// if you aren't familiar with providers, this returns the service
$get: ['$http',
function($http) {
// access configs
var provider = this;
// This is the "class" that your services will "extend"
// whatever parameters you need
// probably best to use some conventions in your server api, to make this simpler
function Listable(name, fields) {
this.name = name;
this.fields = fields;
this.baseUrl = provider.baseUrl; // and other configs
}
// this is the API
Listable.prototype.index = function() {
return $http({
method: 'GET',
url: this.baseUrl + this.name // or something
});
};
Listable.prototype.save = function(data) {
return $http({
method: 'POST',
url: this.baseUrl + this.name,
data: data
});
};
return Listable;
}
]
};
}
]);
这也是保留相关UI组件(如指令和过滤器)的便利场所。
完成后,您可以像这样使用它:
angular.module('MyApp', ['list']).
factory('People', ['Listable',
function(L) {
var People = new L('people', ['first_name', 'last_name', 'dob']);
// you can also override methods of listable, or implement things that may be specific to each data type:
People.create = function() {
return {
// some initial Person object
}
};
return People; // People is a service
}
]);
特别是如果您不使用路由,我认为在服务中实现load
方法以将数据导入控制器非常重要。您不希望尝试将UI元素绑定到服务中的数据,因此您应该在任何时候将其加载到模型中(在我看来)。
同样,我不知道这个实现是否会通过 Angular Best Practice 考试,但是如果你坚持做DRY(就像我一样!),那么这是可能的。