Чи може контролер 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 Cache: 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 Pantry

4
Це працювало для мене, однак я намагаюся зробити це таким чином, щоб мати батьківський контролер і дочірній контролер на одній сторінці. Це призводить до того, що операція $ http у батьківському контролері запускається двічі. Коли дочірній контролер вводить область батьківського контролера мій $ range.AllMembers масив заповнюється вдвічі, оскільки батьківський контролер змушує його запускатись, тоді дочірній контролер змушує його запускатися знову. Чи є спосіб запобігти цьому?
Райан Манн

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всередині успадкованої функції для динамічної зміни контексту.


Хіба ви не могли це зробити замість цього? > контролер $ ('BaseGenericControl', {vm: vm});
оселедець

vmце лише змінна всередині контролера, я не думаю, що Angular міг би використовувати його як очікувалося.
IProblemFactory

8

Я думаю, вам слід скористатися фабрикою або службою, щоб надати доступні функції або дані обом контролерам.

ось подібне питання ---> Успадкування контролера AngularJS


Так, дякую. Я натрапив на цю посаду, коли шукав рішення. Я думав, чи є якийсь спосіб завантажити функцію контролера і розширити «це» за допомогою нього.
До Ка

Я хотів би мати універсальну loadingзмінну, так що коли завантажуються дані, я завжди роблю те саме, я не думаю, що фабрики можуть це робити. Мій батьківський контролер може мати змінну завантаження, але фабрика не може ним маніпулювати ... так ?!
PixMach

7

У відповідь на питання, порушене в цій відповіді gmontague , я знайшов метод успадкування контролера за допомогою $ controller (), і все ще використовую контролер "як" синтаксис.

По-перше, використовуйте синтаксис "як", коли ви успадковуєте виклик $ 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.для отримання.

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

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

Як згадується у прийнятій відповіді, ви можете "успадкувати" зміни батьківського контролера до $ range та інших послуг, зателефонувавши: $controller('ParentCtrl', {$scope: $scope, etc: etc});у своєму дочірньому контролері.

Однак це не вдасться, якщо ви звикли використовувати контролер 'як' синтаксис, наприклад в

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

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

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

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=, оскільки ви вказуєте ім'я екземпляра в коді, а не шаблон.


Використання синтаксису "контролер як" не має жодних проблем. Дивіться моя відповідь: 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>


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);

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.