Может ли контроллер AngularJS наследовать от другого контроллера в том же модуле?

198

Внутри модуля контроллер может наследовать свойства от внешнего контроллера:

var app = angular.module('angularjs-starter', []);

var ParentCtrl = function ($scope, $location) {
};

app.controller('ChildCtrl', function($scope, $injector) {
  $injector.invoke(ParentCtrl, this, {$scope: $scope});
});

Пример через: неработающая ссылка : http://blog.omkarpatil.com/2013/02/controller-inheritance-in-angularjs.html

Может ли контроллер внутри модуля наследовать от родного брата?

var app = angular.module('angularjs-starter', []);

app.controller('ParentCtrl ', function($scope) {
  //I'm the sibling, but want to act as parent
});

app.controller('ChildCtrl', function($scope, $injector) {
  $injector.invoke(ParentCtrl, this, {$scope: $scope}); //This does not work
});

Второй код не работает, так как $injector.invokeтребует функции в качестве первого параметра и не находит ссылку на ParentCtrl.

Федерико Эллес
источник
Это должно помочь: stackoverflow.com/questions/16828287/…
Барт
2
в сторону: это не похоже на наследование, но больше похоже на методы совместного использования или инъекции. Возможно просто семантика.
alockwood05
Ссылка на пример больше не действительна.
AlexS
Ссылка на кэш Google: webcache.googleusercontent.com/…, которая указывает на эту интересную скрипку
Федерико Эллес,

Ответы:

289

Да, это возможно, но вы должны использовать $controllerсервис для создания экземпляра контроллера вместо: -

var app = angular.module('angularjs-starter', []);

app.controller('ParentCtrl', function($scope) {
  // I'm the sibling, but want to act as parent
});

app.controller('ChildCtrl', function($scope, $controller) {
  $controller('ParentCtrl', {$scope: $scope}); //This works
});
Салман фон Аббас
источник
ParentCtrlдолжно быть controllerили можно использовать service?
Gontard
@gontard: в этом случае это должен быть контроллер, так как $controllerможно использовать только зарегистрированные контроллеры.
ZeissS
10
Это очень хорошее решение. Спасибо. Но как мне это сделать, если я использую синтаксис Controller As?
Ка Ка
1
Вышеуказанная скрипка была задана как вопрос. Стоит отметить , что controllerAs просто присваивает контроллер для сферы - Таким образом , вы изменили бы , $scopeчтобы this(в теории)
Dan буфетная
4
Это сработало для меня, однако я пытаюсь сделать это так, чтобы у меня был родительский контроллер и дочерний контроллер на одной странице. Это приводит к тому, что операция $ http в родительском контроллере запускается дважды. Когда дочерний контроллер вводит область действия родительского контроллера, мой массив $ scope.AllMembers get заполняется дважды, поскольку родительский контроллер заставляет его работать, затем дочерний контроллер заставляет его запускаться снова. Есть ли способ предотвратить это?
Райан Манн
20

Если вы используете vmсинтаксис контроллера , вот мое решение:

.controller("BaseGenericCtrl", function ($scope) {

    var vm = this;
    vm.reload = reload;
    vm.items = [];

    function reload() {
        // this function will come from child controller scope - RESTDataService.getItemsA
        this.getItems();
    }
})

.controller("ChildCtrl", function ($scope, $controller, RESTDataService) {
    var vm = this;
    vm.getItems = RESTDataService.getItemsA;
    angular.extend(vm, $controller('BaseGenericCtrl', {$scope: $scope}));
})

К сожалению, вы не можете использовать $controller.call(vm, 'BaseGenericCtrl'...), чтобы передать текущий контекст в reload()функцию замыкания (for ), поэтому только одно решение - использовать thisвнутри унаследованной функции для динамического изменения контекста.

IProblemFactory
источник
Не могли бы вы просто сделать это вместо этого? > $ controller ('BaseGenericControl', {vm: vm});
Херрингтаун
vmэто просто переменная внутри контроллера, я не думаю, что Angular может использовать его как положено.
IProblemFactory
8

Я думаю, вы должны использовать фабрику или сервис, чтобы предоставить доступные функции или данные для обоих контроллеров.

вот похожий вопрос ---> Наследование контроллера AngularJS

LauroSkr
источник
Да, это один из способов, спасибо. Я наткнулся на этот пост, когда искал решение. Я думал, есть ли какой-нибудь способ загрузить функцию контроллера и расширить с ней «это».
Ка Ка
Я хотел бы иметь универсальную loadingпеременную, чтобы при загрузке данных я всегда делал одно и то же, я не думаю, что фабрики могут это делать. Мой родительский контроллер может иметь переменную загрузки, но фабрика не может манипулировать ею ... правильно ?!
PixMach
7

В ответ на вопрос, поднятый в этом ответе от gmontague , я нашел метод для наследования контроллера с помощью $ controller () и все еще использования контроллера «как» синтаксис.

Во-первых, используйте синтаксис «as» при наследовании вызова $ controller ():

    app.controller('ParentCtrl', function(etc...) {
        this.foo = 'bar';
    });
    app.controller('ChildCtrl', function($scope, $controller, etc...) {
        var ctrl = $controller('ParentCtrl as parent', {etc: etc, ...});
        angular.extend(this, ctrl);

    });

Затем, в шаблоне HTML, если свойство определено родительским, тогда используйте parent.для извлечения свойств, унаследованных от родительского; если определено child, используйте его child.для извлечения.

    <div ng-controller="ChildCtrl as child">{{ parent.foo }}</div>
gm2008
источник
5

Ну, я сделал это по-другому. В моем случае я хотел функцию, которая применяет те же функции и свойства в других контроллерах. Мне понравилось, кроме параметров. Таким образом, все ваши ChildCtrls должны получить $ location.

var app = angular.module('angularjs-starter', []);

function BaseCtrl ($scope, $location) {
    $scope.myProp = 'Foo';
    $scope.myMethod = function bar(){ /* do magic */ };
}

app.controller('ChildCtrl', function($scope, $location) {
    BaseCtrl.call(this, $scope, $location);

    // it works
    $scope.myMethod();
});
Фабио Монтефусколо
источник
4

Для тех, кто интересуется, вы можете расширить компонентные контроллеры тем же способом, используя метод в принятом ответе.

Используйте следующий подход:

Родительский компонент (для расширения от):

/**
 * Module definition and dependencies
 */
angular.module('App.Parent', [])

/**
 * Component
 */
.component('parent', {
  templateUrl: 'parent.html',
  controller: 'ParentCtrl',
})

/**
 * Controller
 */
.controller('ParentCtrl', function($parentDep) {

  //Get controller
  const $ctrl = this;

  /**
   * On init
   */
  this.$onInit = function() {

    //Do stuff
    this.something = true;
  };
});

Дочерний компонент (расширяющийся):

/**
 * Module definition and dependencies
 */
angular.module('App.Child', [])

/**
 * Component
 */
.component('child', {
  templateUrl: 'child.html',
  controller: 'ChildCtrl',
})

/**
 * Controller
 */
.controller('ChildCtrl', function($controller) {

  //Get controllers
  const $ctrl = this;
  const $base = $controller('ParentCtrl', {});
  //NOTE: no need to pass $parentDep in here, it is resolved automatically
  //if it's a global service/dependency

  //Extend
  angular.extend($ctrl, $base);

  /**
   * On init
   */
  this.$onInit = function() {

    //Call parent init
    $base.$onInit.call(this);

    //Do other stuff
    this.somethingElse = true;
  };
});

Хитрость заключается в том, чтобы использовать именованные контроллеры вместо определения их в определении компонента.

Адам Рейс
источник
2

Как упоминалось в принятом ответе, вы можете «наследовать» модификации родительского контроллера в $ scope и других сервисах, вызывая: $controller('ParentCtrl', {$scope: $scope, etc: etc});в вашем дочернем контроллере.

Однако это не работает, если вы привыкли использовать контроллер как синтаксис, например, в

<div ng-controller="ChildCtrl as child">{{ child.foo }}</div>

Если он fooбыл установлен в родительском контроллере (через this.foo = ...), дочерний контроллер не будет иметь к нему доступа.

Как уже упоминалось в комментариях, вы можете присвоить результат $ controller непосредственно области:

var app = angular.module('angularjs-starter', []);
app.controller('ParentCtrl ', function(etc...) {
    this.foo = 'bar';
});
app.controller('ChildCtrl', function($scope, $controller, etc...) {
    var inst = $controller('ParentCtrl', {etc: etc, ...});

    // Perform extensions to inst
    inst.baz = inst.foo + " extended";

    // Attach to the scope
    $scope.child = inst;
});

Примечание. Затем необходимо удалить часть «как» ng-controller=, поскольку вы указываете имя экземпляра в коде, а не шаблон.

gmontague
источник
Использование синтаксиса «контроллер как» не имеет проблем. Смотрите мой ответ: stackoverflow.com/a/36549465/2197555
gm2008
2

Я использовал синтаксис «Контроллер как» vm = thisи хотел наследовать контроллер. У меня были проблемы, если у моего родительского контроллера была функция, которая модифицировала переменную.

Используя ответы IProblemFactory и Салмана Аббаса , я сделал следующее, чтобы получить доступ к родительским переменным:

(function () {
  'use strict';
  angular
      .module('MyApp',[])
      .controller('AbstractController', AbstractController)
      .controller('ChildController', ChildController);

  function AbstractController(child) {
    var vm = child;
    vm.foo = 0;
    
    vm.addToFoo = function() {
      vm.foo+=1;
    }
  };
  
  function ChildController($controller) {
    var vm = this;
    angular.extend(vm, $controller('AbstractController', {child: vm}));
  };
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="ChildController as childCtrl" layout="column" ng-cloak="" ng-app="MyApp">
  <button type="button" ng-click="childCtrl.addToFoo()">
    add
  </button>
  <span>
      -- {{childCtrl.foo}} --
  </span>
</div>

Dufaux
источник
0

Вы можете использовать простой механизм наследования JavaScript. Также не забудьте передать необходимые угловые сервисы для вызова метода .call.

//simple function (js class)
function baseCtrl($http, $scope, $location, $rootScope, $routeParams, $log, $timeout, $window, modalService) {//any serrvices and your 2

   this.id = $routeParams.id;
   $scope.id = this.id;

   this.someFunc = function(){
      $http.get("url?id="+this.id)
      .then(success function(response){
        ....
       } ) 

   }
...
}

angular
        .module('app')
        .controller('childCtrl', childCtrl);

//angular controller function
function childCtrl($http, $scope, $location, $rootScope, $routeParams, $log, $timeout, $window, modalService) {      
   var ctrl = this;
   baseCtrl.call(this, $http, $scope, $location, $rootScope,  $routeParams, $log, $timeout, $window, modalService);

   var idCopy = ctrl.id;
   if($scope.id == ctrl.id){//just for sample
      ctrl.someFunc();
   }
}

//also you can copy prototype of the base controller
childCtrl.prototype = Object.create(baseCtrl.prototype);
trueboroda
источник