Quelle est la façon recommandée d'étendre les contrôleurs AngularJS?

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

Peut-être que vous ne prolongez pas de contrôleur, mais il est possible d'étendre un contrôleur ou de faire un seul contrôleur une combinaison de plusieurs contrôleurs.

module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) { // Initialize the super class and extend it. angular.extend(this, $controller('CtrlImpl', {$scope: $scope})); … Additional extensions to create a mixin. }]); 

Lorsque le contrôleur parent est créé, la logique contenue dans celui-ci est également exécutée. Voir $ controller () pour plus d'informations, mais seulement la valeur $scope doit être transmise. Toutes les autres valeurs seront injectées normalement.

@mwarren , votre préoccupation est prise en charge automatiquement par l'injection de dépendance angulaire. Tout ce dont vous avez besoin est d'injecter une portée de $, bien que vous puissiez annuler les autres valeurs injectées si vous le souhaitez. Prenez l'exemple suivant:

 (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); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.js"></script> <div ng-app="stackoverflow.example"> <div ng-controller="complexController as C"> <span><b>Origin from Controller:</b> {{C.getOrigin()}}</span> </div> </div> 

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

 function Parent($scope) { $scope.name = 'Human'; $scope.clickParent = function() { $scope.name = 'Clicked from base controller'; } } function Child($scope, $injector) { $injector.invoke(Parent, this, {$scope: $scope}); $scope.name = 'Human Child'; $scope.clickChild = function(){ $scope.clickParent(); } } Child.prototype = Object.create(Parent.prototype); 

Dans le cas où vous utilisez la syntaxe du controllerAs (ce que je recommande fortement), il est encore plus facile d'utiliser le modèle d'héritage classique:

 function BaseCtrl() { this.name = 'foobar'; } BaseCtrl.prototype.parentMethod = function () { //body }; function ChildCtrl() { BaseCtrl.call(this); this.name = 'baz'; } ChildCtrl.prototype = Object.create(BaseCtrl.prototype); ChildCtrl.prototype.childMethod = function () { this.parentMethod(); //body }; app.controller('BaseCtrl', BaseCtrl); app.controller('ChildCtrl', ChildCtrl); 

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

 function BaseController() { this.click = function () { //some actions here }; } module.controller('ChildCtrl', ['$scope', function ($scope) { BaseController.call($scope); $scope.anotherClick = function () { //other actions }; }]); 

Blog sur ce sujet

Vous n'utilisez pas les contrôleurs. S'ils remplissent 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.

Eh bien, je ne suis pas vraiment sûr de ce que vous voulez atteindre, mais généralement les services sont les chemins à parcourir. Vous pouvez également utiliser les caractéristiques d'héritage de portée de Angular pour partager le code entre les contrôleurs:

 <body ng-controller="ParentCtrl"> <div ng-controller="FirstChildCtrl"></div> <div ng-controller="SecondChildCtrl"></div> </body> function ParentCtrl($scope) { $scope.fx = function() { alert("Hello World"); }); } function FirstChildCtrl($scope) { // $scope.fx() is available here } function SecondChildCtrl($scope) { // $scope.fx() is available here } 

Encore une autre bonne solution tirée de cet article :

 // base controller containing common functions for add/edit controllers module.controller('Diary.BaseAddEditController', function ($scope, SomeService) { $scope.diaryEntry = {}; $scope.saveDiaryEntry = function () { SomeService.SaveDiaryEntry($scope.diaryEntry); }; // add any other shared functionality here. }]) module.controller('Diary.AddDiaryController', function ($scope, $controller) { // instantiate base controller $controller('Diary.BaseAddEditController', { $scope: $scope }); }]) module.controller('Diary.EditDiaryController', function ($scope, $routeParams, DiaryService, $controller) { // instantiate base controller $controller('Diary.BaseAddEditController', { $scope: $scope }); DiaryService.GetDiaryEntry($routeParams.id).success(function (data) { $scope.diaryEntry = data; }); }]); 

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

 app.service("reusableCode", function() { var reusableCode = {}; reusableCode.commonMethod = function() { alert('Hello, World!'); }; return reusableCode; }); 

Ensuite, dans votre contrôleur que vous souhaitez étendre à partir du service réutilisable ci-dessus:

 app.controller('MainCtrl', function($scope, reusableCode) { angular.extend($scope, reusableCode); // now you can access all the properties of reusableCode in this $scope $scope.commonMethod() }); 

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

Vous pouvez essayer quelque chose comme ça (ne pas avoir testé):

 function baseController(callback){ return function($scope){ $scope.baseMethod = function(){ console.log('base method'); } callback.apply(this, arguments); } } app.controller('childController', baseController(function(){ })); 

Vous pouvez utiliser directement $ controller ('ParentController', {$ scope: $ scope}) Exemple de module.controller('Parent', ['$scope', function ($scope) { //code }])

module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) { //extend parent controller $controller('CtrlImpl', {$scope: $scope}); }]);

Vous pouvez étendre avec des services , des usines ou des fournisseurs . Ils sont les mêmes mais avec un degré de flexibilité différent.

Voici un exemple utilisant l'usine: http://jsfiddle.net/aaaflyvw/6KVtj/2/

 angular.module('myApp',[]) .factory('myFactory', function() { var myFactory = { save: function () { // saving ... }, store: function () { // storing ... } }; return myFactory; }) .controller('myController', function($scope, myFactory) { $scope.myFactory = myFactory; myFactory.save(); // here you can use the save function }); 

Et ici, vous pouvez utiliser la fonction de magasin aussi:

 <div ng-controller="myController"> <input ng-blur="myFactory.store()" /> </div> 

Vous pouvez utiliser la syntaxe Angular "comme" combinée à l'héritage JavaScript simple

Voir plus de détails ici http://blogs.microsoft.co.il/oric/2015/01/01/base-controller-angularjs/

J'ai écrit une fonction pour ce faire:

 function extendController(baseController, extension) { return [ '$scope', '$injector', function($scope, $injector) { $injector.invoke(baseController, this, { $scope: $scope }); $injector.invoke(extension, this, { $scope: $scope }); } ] } 

Vous pouvez l'utiliser comme ceci:

 function() { var BaseController = [ '$scope', '$http', // etc. function($scope, $http, // etc. $scope.myFunction = function() { // } // etc. } ]; app.controller('myController', extendController(BaseController, ['$scope', '$filter', // etc. function($scope, $filter /* etc. */) $scope.myOtherFunction = function() { // } // etc. }] ) ); }(); 

Avantages:

  1. Vous ne devez pas enregistrer le contrôleur de base.
  2. Aucun des contrôleurs ne doit connaître les services $ controller ou $ injector.
  3. Cela fonctionne bien avec la syntaxe d'injection de tableau angulaire – ce qui est essentiel si votre javascript va être minifié.
  4. Vous pouvez facilement ajouter des services injectables supplémentaires au contrôleur de base, sans avoir à vous rappeler de les ajouter et de les transmettre à tous vos contrôleurs enfants.

Les inconvénients:

  1. Le contrôleur de base doit être défini comme une variable, qui risque de polluer la portée globale. J'ai évité cela dans mon exemple d'utilisation en enveloppant tout dans une fonction d'auto-exécution anonyme, mais cela signifie que tous les contrôleurs enfants doivent être déclarés dans le même fichier.
  2. Ce modèle fonctionne bien pour les contrôleurs qui sont instanciés directement de votre html, mais ce n'est pas si bon pour les contrôleurs que vous créez à partir de votre code via le service $ controller (), car il dépend de l'injecteur qui vous empêche d'injecter directement, non – paramètres de service à partir de votre code d'appel.

Je considère que l'extension des contrôleurs est une mauvaise pratique. Placer plutôt votre logique partagée dans un service. Les objets étendus en javascript ont tendance à devenir plutôt complexes. Si vous souhaitez utiliser les héritages, je vous recommande dactylographié. Pourtant, les contrôleurs minces sont une meilleure façon d'aller dans mon point de vue.