javascript - AngularJS Controller ,DRY 代码的设计模式

标签 javascript angularjs design-patterns dry angularjs-controller

为了描述这个问题,我创建了一个完整的示例。我的实际应用程序比所提供的演示还要大,并且每个 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 中,我需要一个字段来保存该指令正在修改的值。还有它的设置。后来我需要类似的字段来保存来自数据服务的数据,并且调用本身(方法)也是相同的。

这会导致大量重复。

<小时/>

从图形上看,当前的示例如下所示:

The current design

虽然我相信正确的设计应该看起来更像这样:

The expected design

<小时/>

我试图在这里找到一些解决方案,但似乎没有一个得到证实。我发现了什么:

  1. AngularJS DRY controller structure ,建议我传递 $scope 或 vm 并用额外的方法和字段装饰它。但许多消息来源称这是肮脏的解决方案。
  2. What's the recommended way to extend AngularJS controllers?使用 angular.extend,但是在使用 controller as 语法时会出现问题。
  3. 然后我也找到了答案(在上面的链接中):

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/

相关文章:

javascript - 如何在js代码中获取json值?

javascript - mouseup 事件有问题

javascript - 多角色身份验证 Firebase Web

javascript - Wrapping Foundation 4 在 Angular 中展示

javascript - 正则表达式从下面的字符串捕获日期

angularjs - 如何在 javascript 中使用过滤器而不是 Html

javascript - AngularJS 在另一个函数完成后执行一个函数

php - MVC(Laravel)在哪里添加逻辑

java - 搜索历史和过滤器的面向对象设计

java - 在哪里可以找到使用模式的任务