将重复的控制器逻辑抽象为服务

时间:2015-10-30 17:44:32

标签: javascript angularjs

我有两个控制器和一个指令。两个控制器都具有相同类型的数据,即过滤器阵列。两个控制器使用相同的函数来操作此数据,但这些函数依赖于this(具有“控制器为”语法的范围)。如何将这些功能抽象为服务,但将数据存储在控制器中?我想过将范围转移,但正如我读到的那样,这是不好的做法。有什么想法吗?

.controller('CtrlOne', function() {
  this.filters = [...]; // this data is unique for controller one

  // Want to abstract these into a service
  this.isAllSelected = true;

  this.handleChangeFilter = function(selected) {
    ...
  }.bind(this);

  this.handleShowFilter = function(filter) {
    ...
  }.bind(this);
})

.controller('CtrlTwo', function() {
  this.filters = [...]; // this data is unique for controller two

  // Want to abstract this into a service
  // it is the exact same logic as the other service
  // only thing that changes is `this.filters`
  this.isAllSelected = true;

  this.handleChangeFilter = function(selected) {
    ...
  }.bind(this);

  this.handleShowFilter = function(filter) {
    ...
  }.bind(this);
})

.directive('filtering', function() {
  return {
    restrict: 'E',
    templateUrl: 'filtering.html',
    scope: {},
    bindToController: {
      filters: '=',
      onChangeFilter: '=',
      onShowFilter: '='
    },
    controllerAs: 'filtering',
    controller: function() {
    }
  }
})

// filtering.html
`
<ul>
  <li ng-repeat="filter in filtering.filters"
      ng-click="filtering.onChangeFilter(filter)"
      ng-show="filtering.onShowFilter(filter)">
    <span ng-style="{color: filter.isActive ? 'red' : 'inherit'}">
      {{filter.name}}
    </span>
  </li>
</ul>
`

// Using the filtering directive
`
<filtering
  on-change-filter="::ctrlOne.handleChangeFilter"
  on-show-filter="::ctrlOne.handleShowFilter"
  filters="::ctrlOne.filters">
</filtering>
`

2 个答案:

答案 0 :(得分:0)

我最终做了以下操作,但我对其他解决方案持开放态度,因为我不确定这是否是惯用的Angular:

.controller('CtrlOne', function(filterHandler) {
  this.filters = [...]; // this data is unique for controller one
  this.filterHandler = filterHandler.create(this.filters);
})

.controller('CtrlTwo', function(filterHandler) {
  this.filters = [...]; // this data is unique for controller two
  this.filterHandler = filterHandler.create(this.filters);
})

.directive('filtering', function() {
  return {
    restrict: 'E',
    templateUrl: 'filtering.html',
    scope: {},
    bindToController: {
      filters: '=',
      onChangeFilter: '=',
      onShowFilter: '='
    },
    controllerAs: 'filtering',
    controller: function() {
    }
  }
})

.service('filterHandler', function() {
  return {
    create: function() {
      var isAllSelected = true;
      return {
        change: function(selected) {
          ...
        },
        show: function(filter) {
          ...
        }
      };
    }
  }
})

// filtering.html
`
<ul>
  <li ng-repeat="filter in filtering.filters"
      ng-click="filtering.onChangeFilter(filter)"
      ng-show="filtering.onShowFilter(filter)">
    <span ng-style="{color: filter.isActive ? 'red' : 'inherit'}">
      {{filter.name}}
    </span>
  </li>
</ul>
`

// Using the filtering directive
`
<filtering
  on-change-filter="::ctrlOne.filterHandler.change"
  on-show-filter="::ctrlOne.filterHandler.show"
  filters="::ctrlOne.filters">
</filtering>
`

答案 1 :(得分:0)

I was wrong in my comment about using a service. With a service there arises a chiken-egg problem when you need to inject the service before you create the controller. So I just wrote a globally available base controller function-class and here is a fully working runnable example which I believe is self-explanatory:

function BaseController() {
  this.message = "Base controller message";
  this.init();
  
  // Common constructor code
}

BaseController.prototype = {
  someFunction: function() {
    // This is to show the output (alert does not work in sandbox) 
    this.output = this.message; 
    // Uncomment this to see an alert
    // alert(this.message); 
  }
  ,init: function(){
    throw('Cannot use abstract base class');
  }
};


angular.module('myModule', [])
  .controller('myFirstController', myFirstController)
  .controller('mySecondController', mySecondController);



function myFirstController() {
  BaseController.call(this);
}

myFirstController.prototype = Object.create(BaseController.prototype);

myFirstController.prototype.init = function() {
  this.message = 'First Controller Message';
}


function mySecondController() {
  BaseController.call(this);
  
}

mySecondController.prototype = Object.create(BaseController.prototype);

mySecondController.prototype.init = function() {
  this.message = 'Second Controller Message';
}
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
  </head>
  <body>
    <div ng-controller="myFirstController as ctl">
      <button ng-click="ctl.someFunction()">Call method from first controller</button>
      <div>
      Output: {{ctl.output}}
      </div>
    </div>
    <div ng-controller="mySecondController as ctl">
      <button ng-click="ctl.someFunction()">Call method from second controller</button>
      <div>
      Output: {{ctl.output}}
      </div>
      
    </div>
  </body>