为了描述这个问题,我创建了一个完整的示例。我的实际应用程序比所提供的演示还要大,并且每个 Controller 都运行更多的服务和指令。这会导致更多的代码重复。我尝试添加一些代码注释以进行澄清, PLUNKER:http://plnkr.co/edit/781Phn?p=preview
重复部分:
routerApp.controller('page1Ctrl', function(pageFactory) {
var vm = this;
// page dependent
vm.name = 'theOne';
vm.service = 'oneService';
vm.seriesLabels = ['One1', 'Two1', 'Three1'];
// these variables are declared in all pages
// directive variables,
vm.date = {
date: new Date(),
dateOptions: {
formatYear: 'yy',
startingDay: 1
},
format: 'dd-MMMM-yyyy',
opened: false
};
vm.open = function($event) {
vm.date.opened = true;
};
// dataservice
vm.data = []; // the structure can be different but still similar enough
vm.update = function() {
vm.data = pageFactory.get(vm.service);
}
//default call
vm.update();
})
<小时/>
基本上,我将所有可能的逻辑移至工厂和指令。但是现在,在每个使用特定指令的 Controller 中,我需要一个字段来保存该指令正在修改的值。还有它的设置。后来我需要类似的字段来保存来自数据服务的数据,并且调用本身(方法)也是相同的。
这会导致大量重复。
<小时/>从图形上看,当前的示例如下所示:
虽然我相信正确的设计应该看起来更像这样:
<小时/>我试图在这里找到一些解决方案,但似乎没有一个得到证实。我发现了什么:
- AngularJS DRY controller structure ,建议我传递 $scope 或 vm 并用额外的方法和字段装饰它。但许多消息来源称这是肮脏的解决方案。
- What's the recommended way to extend AngularJS controllers?使用 angular.extend,但是在使用
controller as
语法时会出现问题。 - 然后我也找到了答案(在上面的链接中):
You don't extend controllers. If they perform the same basic functions then those functions need to be moved to a service. That service can be injected into your controllers.
即使我这样做了,仍然有很多重复。还是事情本来就是这样的?就像约翰·帕帕(John Papa)所说(http://www.johnpapa.net/angular-app-structuring-guidelines/):
Try to stay DRY (Don't Repeat Yourself) or T-DRY
您是否遇到过类似的问题?有哪些选择?
最佳答案
从整体设计的 Angular 来看,我认为装饰 Controller 和扩展 Controller 之间没有太大区别。最后,它们都是 mixin 的一种形式,而不是继承。所以这实际上取决于你最舒服的工作方式。重大设计决策之一不仅在于如何将功能传递给所有 Controller ,还在于如何将功能传递给 3 个 Controller 中的 2 个。
工厂装饰
正如您提到的,实现此目的的一种方法是将您的 $scope 或 vm 传递到工厂中,该工厂用额外的方法和字段来装饰您的 Controller 。我不认为这是一个肮脏的解决方案,但我可以理解为什么有些人希望将工厂与他们的 $scope 分开,以便分离他们的代码的关注点。如果您需要向 3 中 2 场景添加附加功能,则可以传入其他工厂。我做了一个plunker example of this 。
dataservice.js
routerApp.factory('pageFactory', function() {
return {
setup: setup
}
function setup(vm, name, service, seriesLabels) {
// page dependent
vm.name = name;
vm.service = service;
vm.seriesLabels = seriesLabels;
// these variables are declared in all pages
// directive variables,
vm.date = {
date: moment().startOf('month').valueOf(),
dateOptions: {
formatYear: 'yy',
startingDay: 1
},
format: 'dd-MMMM-yyyy',
opened: false
};
vm.open = function($event) {
vm.date.opened = true;
};
// dataservice
vm.data = []; // the structure can be different but still similar enough
vm.update = function() {
vm.data = get(vm.service);
}
//default call
vm.update();
}
});
page1.js
routerApp.controller('page1Ctrl', function(pageFactory) {
var vm = this;
pageFactory.setup(vm, 'theOne', 'oneService', ['One1', 'Two1', 'Three1']);
})
扩展 Controller
您提到的另一个解决方案是扩展 Controller 。这可以通过创建一个混合到正在使用的 Controller 中的 super Controller 来实现。如果您需要向特定 Controller 添加附加功能,您只需混合具有特定功能的其他 super Controller 即可。这是plunker example 。
父页面
routerApp.controller('parentPageCtrl', function(vm, pageFactory) {
setup()
function setup() {
// these variables are declared in all pages
// directive variables,
vm.date = {
date: moment().startOf('month').valueOf(),
dateOptions: {
formatYear: 'yy',
startingDay: 1
},
format: 'dd-MMMM-yyyy',
opened: false
};
vm.open = function($event) {
vm.date.opened = true;
};
// dataservice
vm.data = []; // the structure can be different but still similar enough
vm.update = function() {
vm.data = pageFactory.get(vm.service);
}
//default call
vm.update();
}
})
page1.js
routerApp.controller('page1Ctrl', function($controller) {
var vm = this;
// page dependent
vm.name = 'theOne';
vm.service = 'oneService';
vm.seriesLabels = ['One1', 'Two1', 'Three1'];
angular.extend(this, $controller('parentPageCtrl', {vm: vm}));
})
嵌套状态 UI 路由器
由于您使用的是 ui-router,因此您也可以通过嵌套状态获得类似的结果。对此需要注意的是,$scope 不会从父 Controller 传递到子 Controller 。因此,您必须在 $rootScope 中添加重复的代码。当我想要传递整个程序的功能时,我会使用它,例如测试我们是否在手机上的功能,该功能不依赖于任何 Controller 。这是plunker example .
关于javascript - AngularJS Controller ,DRY 代码的设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33918753/