197 votes

Quelle est la méthode recommandée pour étendre les contrôleurs AngularJS ?

J'ai trois contrôleurs qui sont assez similaires. Je veux avoir un contrôleur que ces trois étendent et partagent ses fonctions.

304voto

Enzey Points 481

Peut-être que vous n'étendez pas un contrôleur mais il est possible d'étendre un contrôleur ou de faire d'un contrôleur unique un mixin de plusieurs contrôleurs.

module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) {
    // Initialiser la super classe et l'étendre.
    angular.extend(this, $controller('CtrlImpl', {$scope: $scope}));
    … Extensions supplémentaires pour créer un mixin.
}]);

Lorsque le contrôleur parent est créé, la logique qu'il contient est également exécutée. Voir $controller() pour plus d'informations à ce sujet, mais seule la valeur $scope doit être passée. Toutes les autres valeurs seront injectées normalement.

@mwarren, votre préoccupation est prise en charge automatiquement par l'injection de dépendance Angular. Tout ce que vous avez à faire est d'injecter $scope, bien que vous puissiez remplacer les autres valeurs injectées si vous le souhaitez. Voici un exemple:

(function(angular) {

    var module = angular.module('stackoverflow.example',[]);

    module.controller('simpleController', function($scope, $document) {
        this.getOrigin = function() {
            return $document[0].location.origin;
        };
    });

    module.controller('complexController', function($scope, $controller) {
        angular.extend(this, $controller('simpleController', {$scope: $scope}));
    });

})(angular);

        Origine depuis le Contrôleur: {{C.getOrigin()}}

Bien que $document ne soit pas passé dans 'simpleController' lorsqu'il est créé par 'complexController', $document est injecté pour nous.

1 votes

De loin la solution la plus rapide, la plus propre et la plus facile pour cela! Merci!

0 votes

Parfait, solution incroyable !

8 votes

Je pense que vous n'avez pas besoin de $.extend(), vous pouvez simplement appeler $controller('CtrlImpl', {$scope: $scope});

52voto

Minko Gechev Points 11295

Pour l'héritage, vous pouvez utiliser des modèles d'héritage JavaScript standard. Voici une démo qui utilise $injector

function Parent($scope) {
  $scope.name = 'Humain';
  $scope.clickParent = function() {
    $scope.name = 'Cliqué depuis le contrôleur de base';
  }    
}

function Child($scope, $injector) {
  $injector.invoke(Parent, this, {$scope: $scope});
  $scope.name = 'Enfant humain';
  $scope.clickChild = function(){
    $scope.clickParent();
  }       
}

Child.prototype = Object.create(Parent.prototype);

Si vous utilisez la syntaxe controllerAs (que je recommande vivement), il est encore plus facile d'utiliser le modèle d'héritage classique :

function BaseCtrl() {
  this.name = 'foobar';
}
BaseCtrl.prototype.parentMethod = function () {
  //corps
};

function ChildCtrl() {
  BaseCtrl.call(this);
  this.name = 'baz';
}
ChildCtrl.prototype = Object.create(BaseCtrl.prototype);
ChildCtrl.prototype.childMethod = function () {
  this.parentMethod();
  //corps
};

app.controller('BaseCtrl', BaseCtrl);
app.controller('ChildCtrl', ChildCtrl);

Une autre façon pourrait être de créer simplement une fonction constructeur "abstraite" qui sera votre contrôleur de base :

function BaseController() {
  this.click = function () {
    //actions ici
  };
}

module.controller('ChildCtrl', ['$scope', function ($scope) {
  BaseController.call($scope);
  $scope.anotherClick = function () {
    //autres actions
  };
}]);

Article de blog sur ce sujet

16voto

Andre Goncalves Points 1735

Eh bien, je ne suis pas tout à fait sûr de ce que vous voulez réaliser, mais en général, les Services sont la meilleure solution. Vous pouvez également utiliser les caractéristiques de l'héritage de portée d'Angular pour partager du code entre les contrôleurs:

function ParentCtrl($scope) {
 $scope.fx = function() {
   alert("Bonjour le monde");
 });
}

function FirstChildCtrl($scope) {
  // $scope.fx() est disponible ici
}

function SecondChildCtrl($scope) {
  // $scope.fx() est disponible ici
}

0 votes

Partager les mêmes variables et fonctions entre les contrôleurs qui font des choses similaires (l'un est pour l'édition, un autre pour la création, etc.). C'est certainement l'une des solutions...

1 votes

$scope héritage est de loin la meilleure façon de le faire, bien mieux que la réponse acceptée.

0 votes

Au final, cela semblait être la façon la plus angulaire d'aller. J'ai trois parentControllers très brefs sur trois pages différentes qui définissent simplement une valeur $scope que le childController peut récupérer. Le childController, que j'avais initialement envisagé d'étendre, contient toute la logique du contrôleur.

15voto

Bart Points 7738

Vous ne devez pas étendre les contrôleurs. Si ces derniers effectuent les mêmes fonctions de base, ces fonctions doivent être déplacées vers un service. Ce service peut être injecté dans vos contrôleurs.

4 votes

Merci, mais j'ai 4 fonctions qui utilisent déjà des services (enregistrer, supprimer, etc.) et qui existent dans les trois contrôleurs. Si l'extension n'est pas une option, y a-t-il une possibilité pour un 'mixin' ?

0 votes

Aussi, il y a des variables que je ne veux pas définir trois fois.

0 votes

Ne mélangez pas. Vous pourriez créer un autre service qui gère ces méthodes de service (enregistrer, supprimer, ...). Plus vous pourrez déplacer la logique d'application hors de votre contrôleur, mieux ce sera.

7voto

Raghav Points 1068

Vous pouvez créer un service et hériter de son comportement dans n'importe quel contrôleur simplement en l'injectant.

app.service("reusableCode", function() {

    var reusableCode = {};

    reusableCode.commonMethod = function() {
        alert('Bonjour, le Monde !');
    };

    return reusableCode;
});

Ensuite, dans votre contrôleur où vous souhaitez étendre le service reusableCode ci-dessus :

app.controller('MainCtrl', function($scope, reusableCode) {

    angular.extend($scope, reusableCode);

    // maintenant vous pouvez accéder à toutes les propriétés de reusableCode dans ce $scope
    $scope.commonMethod()

});

DEMO PLUNKER: http://plnkr.co/edit/EQtj6I0X08xprE8D0n5b?p=preview

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X