Qual é a maneira recomendada de estender os controladores AngularJS?

Eu tenho três controladores que são bastante semelhantes. Eu quero ter um controlador que estes três se estendem e compartilham suas funções.

Talvez você não estenda um controlador, mas é possível estender um controlador ou tornar um único controlador uma mistura de vários controladores.

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

Quando o controlador pai é criado, a lógica contida nele também é executada. Veja $ controller () para obter mais informações sobre, mas somente o valor $scope precisa ser passado. Todos os outros valores serão injetados normalmente.

@mwarren , sua preocupação é cuidada de forma automática por injeção de dependência Angular. Tudo que você precisa é injetar $ escopo, embora você possa replace os outros valores injetados, se desejar. Tome o seguinte exemplo:

 (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); 
  
Origin from Controller: {{C.getOrigin()}}

Para inheritance, você pode usar padrões de inheritance JavaScript padrão. Aqui está uma demonstração que usa $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); 

Caso você use a syntax controllerAs (que eu recomendo), é ainda mais fácil usar o padrão de inheritance clássica:

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

Outra maneira seria criar apenas uma function construtora “abstrata”, que será o seu controlador de base:

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

Postagem do blog sobre este tópico

Bem, eu não sei exatamente o que você quer alcançar, mas geralmente os Serviços são o caminho a percorrer. Você também pode usar as características de inheritance do escopo do Angular para compartilhar o código entre os controladores:

  
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 }

Você não estende os controladores. Se eles executam as mesmas funções básicas, essas funções precisam ser movidas para um serviço. Esse serviço pode ser injetado em seus controladores.

Ainda outra boa solução retirada deste artigo :

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

Você pode criar um serviço e herdar seu comportamento em qualquer controlador apenas injetando-o.

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

Em seguida, no seu controlador que você deseja estender do serviço reusableCode acima:

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

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

Você pode tentar algo assim (não testei):

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

Você pode usar diretamente $ controller (‘ParentController’, {$ scope: $ scope}) Exemplo module.controller('Parent', ['$scope', function ($scope) { //code }])

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

Você pode estender com serviços , fábricas ou fornecedores . eles são os mesmos, mas com diferentes graus de flexibilidade.

aqui um exemplo usando a fábrica: 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 }); 

E aqui você pode usar a function de armazenamento também:

 

Você pode usar a syntax Angular “as” combinada com a inheritance simples do JavaScript

Veja mais detalhes aqui http://blogs.microsoft.co.il/oric/2015/01/01/base-controller-angularjs/

Eu escrevi uma function para fazer isso:

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

Você pode usá-lo assim:

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

Prós:

  1. Você não precisa registrar o controlador de base.
  2. Nenhum dos controladores precisa saber sobre os serviços $ controller ou $ injector.
  3. Ele funciona bem com a syntax de injeção de matriz do angular – que é essencial se o seu javascript for reduzido.
  4. Você pode adicionar facilmente serviços extras injetáveis ​​ao controlador de base, sem ter que lembrar de adicioná-los e passá-los de todos os seus controladores filhos.

Contras:

  1. O controlador de base deve ser definido como uma variável, o que pode poluir o escopo global. Eu evitei isso no meu exemplo de uso envolvendo tudo em uma function auto-executável anônima, mas isso significa que todos os controladores filhos devem ser declarados no mesmo arquivo.
  2. Esse padrão funciona bem para controladores que são instanciados diretamente do seu html, mas não é tão bom para os controladores que você cria a partir do seu código através do serviço $ controller (), porque a dependência do injetor impede que você injete diretamente extra parameters de serviço do seu código de chamada.

Considero estender os controladores como prática incorreta. Em vez disso, coloque sua lógica compartilhada em um serviço. Objetos estendidos em javascript tendem a ficar bastante complexos. Se você quiser usar inheritance, eu recomendaria o typescript. Ainda assim, controladores finos são a melhor maneira de ir no meu ponto de vista.