Un controller AngularJS può ereditare da un altro controller nello stesso modulo?


198

All'interno di un modulo, un controller può ereditare le proprietà da un controller esterno:

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

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

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

Esempio tramite: Dead link : http://blog.omkarpatil.com/2013/02/controller-inheritance-in-angularjs.html

Anche un controller all'interno di un modulo può ereditare da un fratello?

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

Il secondo codice non funziona poiché $injector.invokerichiede una funzione come primo parametro e non trova il riferimento a ParentCtrl.



2
a parte: questo non sembra ereditarietà, ma più come metodi di condivisione o iniezione. Forse solo semantica.
alockwood05,

Il collegamento per l'esempio non è più valido.
AlexS,

Google Cache Link: webcache.googleusercontent.com/… che punta a questo interessante violino: jsfiddle.net/mhevery/u6s88/12
Federico Elles,

Risposte:


289

Sì, può ma devi usare il $controllerservizio per istanziare il controller invece: -

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

ParentCtrldovrebbe essere un controllero è possibile usare un service?
Gontard,

@gontard: in questo caso deve essere un controller, poiché $controllerpuò utilizzare solo controller registrati.
ZeissS,

10
È un'ottima soluzione. Grazie. Ma come lo farei se sto usando la sintassi Controller As?
A Ka il

1
Il violino sopra è stato posto come una domanda. Vale la pena notare che il controller assegna semplicemente il controller all'ambito - Quindi dovresti passare $scopea this(in teoria)
Dan Pantry

4
Questo ha funzionato per me, tuttavia sto provando a farlo in modo da avere il controller principale e il controller secondario nella stessa pagina. Questo fa funzionare due volte l'operazione $ http nel controller principale. Quando il controller figlio inietta l'ambito del controller padre my $ scope.AllMembers array viene popolato due volte quando il controller padre ne fa funzionare, quindi il controller figlio ne esegue nuovamente l'esecuzione. C'è un modo per impedirlo?
Ryan Mann,

20

Nel caso in cui si stia utilizzando la vmsintassi del controller , ecco la mia soluzione:

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

Sfortunatamente, non è possibile utilizzare $controller.call(vm, 'BaseGenericCtrl'...), per passare il contesto corrente alla funzione di chiusura (per reload()), quindi solo una soluzione è quella di utilizzare la thisfunzione ereditata al fine di cambiare dinamicamente il contesto.


Non avresti potuto farlo invece? > $ controller ('BaseGenericControl', {vm: vm});
Herringtown

vmè solo una variabile all'interno del controller, non credo che Angular possa usarlo come previsto.
IProblemFactory

8

Penso che dovresti usare la fabbrica o il servizio per fornire funzioni o dati accessibili per entrambi i controller.

ecco una domanda simile ---> ereditarietà del controller AngularJS


Sì, è un modo, grazie. Mi sono imbattuto in quel post mentre cercavo una soluzione. Stavo pensando se c'era un modo per caricare la funzione del controller ed estendere "questo" con esso.
A Ka il

Vorrei avere una loadingvariabile universale in modo che quando i dati vengono caricati faccio sempre la stessa cosa, non penso che le fabbriche possano farlo. Il mio controller genitore può avere una variabile di caricamento ma la fabbrica non può manipolarla ... giusto ?!
PixMach

7

In risposta al problema sollevato in questa risposta da gmontague , ho trovato un metodo per ereditare un controller usando $ controller (), e ancora uso il controller "come" sintassi.

In primo luogo, utilizzare la sintassi "come" quando si eredita la chiamata a $ 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);

    });

Quindi, nel modello HTML, se la proprietà è definita dal padre, utilizzare parent.per recuperare le proprietà ereditate dal padre; se definito da figlio, utilizzare child.per recuperarlo.

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

5

Bene, l'ho fatto in un altro modo. Nel mio caso volevo una funzione che applica le stesse funzioni e proprietà in altri controller. Mi è piaciuto, tranne che per parametri. In questo modo, tutti i tuoi ChildCtrls devono ricevere $ 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

Per chi si chiede, è possibile estendere i controller dei componenti nello stesso modo, usando il metodo nella risposta accettata.

Utilizzare il seguente approccio:

Componente padre (da estendere):

/**
 * 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;
  };
});

Componente figlio (quello che si estende):

/**
 * 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;
  };
});

Il trucco è usare controller nominati, invece di definirli nella definizione del componente.


2

Come menzionato nella risposta accettata, puoi "ereditare" le modifiche di un controllore principale in $ scope e altri servizi chiamando: $controller('ParentCtrl', {$scope: $scope, etc: etc});nel tuo controllore figlio.

Tuttavia , ciò non riesce se si è abituati a utilizzare la sintassi del controller "as", ad esempio in

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

Se è foostato impostato nel controller padre (tramite this.foo = ...), il controller figlio non avrà accesso ad esso.

Come menzionato nei commenti, è possibile assegnare il risultato di $ controller direttamente all'ambito:

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

Nota: è quindi necessario rimuovere la parte 'as' da ng-controller=, poiché si sta specificando il nome dell'istanza nel codice e non più il modello.


L'uso della sintassi "controller as" non ha alcun problema. Vedere la mia risposta: stackoverflow.com/a/36549465/2197555
gm2008

2

Stavo usando la sintassi "Controller as" con vm = thise volevo ereditare un controller. Ho avuto problemi se il mio controller genitore aveva una funzione che modificava una variabile.

Utilizzando le risposte di IProblemFactory e Salman Abbas , ho fatto quanto segue per avere accesso alle variabili parent:

(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

È possibile utilizzare un semplice meccanismo di ereditarietà JavaScript. Inoltre, non dimenticare di passare un servizio angolare necessario per invocare il metodo .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);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.