AngularJS:跨多个资源重用控制器的最佳实践

时间:2014-10-21 14:50:39

标签: angularjs

我有几个简单的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);
    };

}
]);

干扰控制器的最佳方法是什么,以便我可以为任意资源重用相同的代码?

1 个答案:

答案 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(就像我一样!),那么这是可能的。