Как сделать прототип из 2 одинаковых контроллеров в angularjs?

В моем приложении 2 практически одинаковых контроллера. Многие функции одинаковы, поэтому я бы хотел их прототипировать. Это контроллер № 1:

c2gcontroller.js

angular.module('c2gyoApp')
  .controller('C2gCtrl', function($scope) {
    // some unique stuff
    $scope.feeDay = 59;
    ...
    // the identical functions
    $scope.getMinutes = function(minutes) {
      var duration = moment.duration(minutes, 'm');
      return duration.minutes();
    };
    ...
  });

и контроллер № 2:

c2gbcontroller.js

angular.module('c2gyoApp')
  .controller('C2gbCtrl', function($scope) {
    // some unique stuff
    $scope.feeDay = 89;
    ...
    // the identical functions
    $scope.getMinutes = function(minutes) {
      var duration = moment.duration(minutes, 'm');
      return duration.minutes();
    };
    ...
  });

Я пытался положить $scope.getMinutes в завод:

smfactory.js

angular.module('c2gyoApp')
  .factory('smfactory', function() {
    return {
      getHours: function(minutes) {
        var duration = moment.duration(minutes, 'm');
        return Math.ceil(duration.asHours() % 24);
      }
    };
  });

Я ввел smfactory в c2gcontroller.js

c2gcontroller.js (попытка №1)

angular.module('c2gyoApp')
  .controller('C2gCtrl', function($scope, smfactory) {
    ...
    // the identical functions
    $scope.getHours = smfactory.getHours(minutes);
    ...
  });

Это приводит к ошибке, что минуты не определены

 line 33  col 42  'minutes' is not defined.

Итак, я попробовал:

c2gcontroller.js (попытка № 2)

angular.module('c2gyoApp')
  .controller('C2gCtrl', function($scope, smfactory) {
    ...
    // the identical functions
    $scope.getMinutes = function(minutes) {
      return smfactory.getHours(minutes);
    };
    ...
  });

что не приводит к ошибке, но мое приложение перестало отвечать на запросы. В принципе $scope.getMinutes ничего не возвращает сейчас.

Я много читал и смотрел об услугах, фабриках, провайдерах AngularJS, но я не знаю, куда идти дальше. Какой будет правильный способ создания прототипа? c2gcontroller.js а также c2gbcontroller.js?

2 ответа

Решение

Как насчет псевдо-наследования с angular.extend

/* define a "base" controller with shared functionality */
.controller('baseCtrl', ['$scope', .. 
    function($scope, ...) {

  $scope.getMinutes = function(minutes) {
    var duration = moment.duration(minutes, 'm');
    return duration.minutes();
  };


.controller('C2gCtrl', ['$controller', '$scope', ...
    function($controller, $scope, ...) {

  // copies the functionality from baseCtrl to this controller
  angular.extend(this, $controller('baseCtrl', {$scope: $scope}));

  // some unique stuff
  $scope.feeDay = 59;

})

.controller('C2gbCtrl', ['$controller', '$scope', ...
    function($controller, $scope, ...) {

  // copies the functionality from baseCtrl to this controller
  angular.extend(this, $controller('baseCtrl', {$scope: $scope}))

  // some unique stuff
  $scope.feeDay = 89;
})

Вот где используется сочетание потрясающей JavaScript, и controller as Синтаксис действительно пригодится.

Если мы вытащим ваши общие функции из простого старого объекта:

var commonStuff = {
   getHours: function(minutes) {
        var duration = moment.duration(minutes, 'm');
        return Math.ceil(duration.asHours() % 24);
   }
};

Затем, если мы реорганизуем наш контроллер в обычный JS-объект, мы можем дополнить его микшированием одним из двух способов. Либо непосредственно на сам объект, либо через прототип.

//Using the instance
function MyCtrl(){
   var vm = this;

   angular.extend(vm, commonStuff);

   //Other stuff
}

//Or via the prototype
function MyCtrl(){
   var vm = this;
}

//Controller specific
MyCtrl.prototype = {

};

angular.extend(MyCtrl.prototype, commonStuff);

Самым большим отличием является то, что теперь вы можете просто ссылаться на контроллер напрямую с помощью controller as синтаксис.

<div ng-controller="myCtrl as ctrl">
  <a href="" ng-click="ctrl.getHours(120)">Get Hours</a>
</div>