Kann ein AngularJS-Controller von einem anderen Controller im selben Modul erben?

198

Innerhalb eines Moduls kann ein Controller Eigenschaften von einem externen Controller erben:

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

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

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

Beispiel über: Dead Link : http://blog.omkarpatil.com/2013/02/controller-inheritance-in-angularjs.html

Kann auch ein Controller in einem Modul von einem Geschwister erben?

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

Der zweite Code funktioniert nicht, da $injector.invokeeine Funktion als erster Parameter erforderlich ist und der Verweis auf nicht gefunden wird ParentCtrl.

Federico Elles
quelle
Dies sollte helfen: stackoverflow.com/questions/16828287/…
Bart
2
Abgesehen davon: Dies sieht nicht nach Vererbung aus, sondern eher nach Teilen von Methoden oder Injizieren. Vielleicht nur Semantik.
Alockwood05
Der Link für das Beispiel ist nicht mehr gültig.
AlexS
Google Cache Link: webcache.googleusercontent.com/…, der auf diese interessante Geige verweist
Federico Elles

Antworten:

289

Ja, es kann, aber Sie müssen $controllerstattdessen den Dienst verwenden, um den Controller zu instanziieren: -

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
});
Salman von Abbas
quelle
ParentCtrlsollte ein sein controlleroder ist es möglich ein zu verwenden service?
Pontard
@gontard: In diesem Fall muss es sich um einen Controller handeln, da $controllernur registrierte Controller verwendet werden können.
ZeissS
10
Es ist eine sehr gute Lösung. Danke dir. Aber wie würde ich es machen, wenn ich Controller As-Syntax verwende?
Zu Ka
1
Die obige Geige wurde als Frage gestellt. Es ist erwähnenswert, dass controllerAs einfach weist den Controller an den Rahmen - also Sie ändern würde $scopezu this(in der Theorie)
Dan Pantry
4
Dies hat bei mir funktioniert, aber ich versuche dies so, dass ich den übergeordneten Controller und den untergeordneten Controller auf derselben Seite habe. Dies führt dazu, dass die $ http-Operation im übergeordneten Controller zweimal ausgeführt wird. Wenn der untergeordnete Controller den Bereich des übergeordneten Controllers in mein $ scope.AllMembers-Array einfügt, wird er zweimal ausgefüllt, wenn der übergeordnete Controller die Ausführung veranlasst, und der untergeordnete Controller bewirkt, dass er erneut ausgeführt wird. Gibt es eine Möglichkeit, dies zu verhindern?
Ryan Mann
20

Wenn Sie die vmController-Syntax verwenden , ist hier meine Lösung:

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

Leider können Sie nicht verwenden $controller.call(vm, 'BaseGenericCtrl'...), um den aktuellen Kontext an die Closure (for reload()) -Funktion zu übergeben. Daher besteht nur eine Lösung darin, thisinnerhalb der geerbten Funktion zu verwenden, um den Kontext dynamisch zu ändern.

IProblemFactory
quelle
Könnten Sie das nicht einfach stattdessen tun? > $ controller ('BaseGenericControl', {vm: vm});
Herringtown
vmist nur eine Variable innerhalb des Controllers, ich glaube nicht, dass Angular sie wie erwartet verwenden könnte.
IProblemFactory
8

Ich denke, Sie sollten die Fabrik oder den Service verwenden, um zugängliche Funktionen oder Daten für beide Controller bereitzustellen.

Hier ist eine ähnliche Frage ---> AngularJS-Controller-Vererbung

LauroSkr
quelle
Ja, das ist eine Möglichkeit, danke. Ich bin auf diesen Beitrag gestoßen, als ich nach einer Lösung gesucht habe. Ich dachte, ob es eine Möglichkeit gibt, die Controller-Funktion zu laden und "dies" damit zu erweitern.
Zu Ka
Ich hätte gerne eine universelle loadingVariable, damit ich beim Laden von Daten immer das Gleiche mache. Ich glaube nicht, dass Fabriken das können. Mein übergeordneter Controller kann eine Ladevariable haben, aber die Fabrik kann sie nicht manipulieren ... richtig?!
PixMach
7

Als Antwort auf das in dieser Antwort von gmontague angesprochene Problem habe ich eine Methode gefunden, um einen Controller mit $ controller () zu erben und den Controller weiterhin als Syntax "as" zu verwenden.

Verwenden Sie zunächst die Syntax "as", wenn Sie den Aufruf von $ controller () erben:

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

    });

Wenn die Eigenschaft in einer HTML-Vorlage vom übergeordneten Element definiert wird, verwenden Sie parent.diese Option, um vom übergeordneten Element geerbte Eigenschaften abzurufen. Wenn vom Kind definiert, verwenden Sie es child., um es abzurufen.

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

Nun, ich habe das anders gemacht. In meinem Fall wollte ich eine Funktion, die dieselben Funktionen und Eigenschaften in anderen Controllern anwendet. Mir hat es gefallen, außer nach Parametern. Auf diese Weise müssen alle Ihre ChildCtrls $ location erhalten.

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();
});
Fabio Montefuscolo
quelle
4

Wenn Sie sich fragen, können Sie Komponenten-Controller auf dieselbe Weise erweitern, indem Sie die Methode in der akzeptierten Antwort verwenden.

Verwenden Sie den folgenden Ansatz:

Übergeordnete Komponente (zu erweitern von):

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

Untergeordnete Komponente (die erweiterte):

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

Der Trick besteht darin, benannte Controller zu verwenden, anstatt sie in der Komponentendefinition zu definieren.

Adam Reis
quelle
2

Wie in der akzeptierten Antwort erwähnt, können Sie die Änderungen eines übergeordneten Controllers an $ scope und anderen Diensten "erben", indem Sie Folgendes aufrufen: $controller('ParentCtrl', {$scope: $scope, etc: etc});in Ihrem untergeordneten Controller.

Dies schlägt jedoch fehl, wenn Sie es gewohnt sind, die Controller-Syntax 'as' zu verwenden, z. B. in

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

Wenn dies fooim übergeordneten Controller (via this.foo = ...) festgelegt wurde, hat der untergeordnete Controller keinen Zugriff darauf.

Wie in den Kommentaren erwähnt, können Sie das Ergebnis von $ controller direkt dem Bereich zuordnen:

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

Hinweis: Sie müssen dann den Teil 'as' aus entfernen ng-controller=, da Sie den Instanznamen im Code und nicht mehr die Vorlage angeben.

gmontague
quelle
Die Verwendung der Syntax "controller as" ist kein Problem. Siehe meine Antwort: stackoverflow.com/a/36549465/2197555
gm2008
2

Ich habe die Syntax "Controller as" mit verwendet vm = this und wollte einen Controller erben. Ich hatte Probleme, wenn mein übergeordneter Controller eine Funktion hatte, die eine Variable änderte.

Mit den Antworten von IProblemFactory und Salman Abbas habe ich Folgendes getan, um Zugriff auf übergeordnete Variablen zu erhalten:

(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
quelle
0

Sie können einen einfachen JavaScript-Vererbungsmechanismus verwenden. Vergessen Sie auch nicht, einen erforderlichen Winkeldienst zu übergeben, um die .call-Methode aufzurufen.

//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
quelle