Каков рекомендуемый способ расширения контроллеров AngularJS?
У меня есть три контроллера, которые очень похожи. Я хочу иметь контроллер, который эти три расширяют и разделяют его функции.
12 ответов:
возможно вы не расширяйте контроллер, но можно расширить контроллер или сделать один контроллер смесью нескольких контроллеров.
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. }]);
при создании родительского контроллера также выполняется логика, содержащаяся в нем. См. $controller () для получения дополнительной информации, но только
$scope
значение должно быть передано. Все остальные значения будут введены нормально.@mwarren, ваша забота позаботился авто-магически путем инъекции угловой зависимости. Все, что вам нужно, это ввести $scope, хотя при желании вы можете переопределить другие введенные значения. Возьмем следующий пример:
(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>
хотя $document не передается в "simpleController", когда он создается "complexController" $document вводится для нас.
для наследования можно использовать стандартные шаблоны наследования JavaScript. Вот демо, которое использует
$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);
в случае, если вы используете
controllerAs
синтаксис (который я настоятельно рекомендую), еще проще использовать классический шаблон наследования: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);
другим способом может быть создание просто "абстрактной" функции конструктора, которая будет вашим базовым контроллером:
function BaseController() { this.click = function () { //some actions here }; } module.controller('ChildCtrl', ['$scope', function ($scope) { BaseController.call($scope); $scope.anotherClick = function () { //other actions }; }]);
Ну, я не совсем уверен, что вы хотите достичь, но обычно услуги-это путь. Вы также можете использовать характеристики наследования области углового для совместного использования кода между контроллерами:
<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 }
вы не расширяете контроллеры. Если они выполняют одни и те же основные функции, то эти функции должны быть перемещены в службу. Эта услуга может быть введена в контроллеры.
еще одно хорошее решение, взятое из этого статьи:
// 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; }); }]);
вы можете создать сервис и унаследовать его поведение в любом контроллере, просто введя его.
app.service("reusableCode", function() { var reusableCode = {}; reusableCode.commonMethod = function() { alert('Hello, World!'); }; return reusableCode; });
затем в вашем контроллере, который вы хотите расширить из вышеуказанной службы reusableCode:
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
вы можете попробовать что-то вроде этого (не проверял):
function baseController(callback){ return function($scope){ $scope.baseMethod = function(){ console.log('base method'); } callback.apply(this, arguments); } } app.controller('childController', baseController(function(){ }));
вы можете напрямую использовать $controller ('ParentController', {$scope:$scope}) Образец
module.controller('Parent', ['$scope', function ($scope) { //code }])
module.controller('CtrlImplAdvanced', ['$scope', '$controller', function ($scope, $controller) { //extend parent controller $controller('CtrlImpl', {$scope: $scope}); }]);
вы можете расширить с помощью услуги,фабрики или поставщики. они одинаковы, но с разной степенью гибкости.
вот пример использования фабрики: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 });
и здесь вы можете использовать функцию магазина:
<div ng-controller="myController"> <input ng-blur="myFactory.store()" /> </div>
вы можете использовать угловой синтаксис "как" в сочетании с простым наследованием JavaScript
Подробнее смотрите здесь http://blogs.microsoft.co.il/oric/2015/01/01/base-controller-angularjs/
Я написал функцию для этого:
function extendController(baseController, extension) { return [ '$scope', '$injector', function($scope, $injector) { $injector.invoke(baseController, this, { $scope: $scope }); $injector.invoke(extension, this, { $scope: $scope }); } ] }
Вы можете использовать его как это:
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. }] ) ); }();
плюсы:
- вам не нужно регистрировать базовый контроллер.
- ни один из контроллеров не должен знать о сервисах $controller или $injector.
- он хорошо работает с синтаксисом ввода массива angular-что важно, если ваш javascript будет уменьшен.
- вы можете легко добавить дополнительные инъекционные услуги для базового контроллера,без также нужно помнить, чтобы добавить их и передать их через все ваши дочерние контроллеры.
плюсы:
- базовый контроллер должен быть определен как переменная, которая рискует загрязнить глобальную область. Я избежал этого в своем примере использования, обернув все в анонимную самоисполняющуюся функцию, но это означает, что все дочерние контроллеры имеют быть объявленным в том же файле.
- этот шаблон хорошо работает для контроллеров, которые создаются непосредственно из вашего html, но не так хорош для контроллеров, которые вы создаете из своего кода через службу $controller (), потому что зависимость от инжектора не позволяет вам напрямую вводить дополнительные, несервисные параметры из вашего вызывающего кода.
Я рассматриваю расширение контроллеров как плохую практику. Скорее поместите свою общую логику в службу. Расширенные объекты в javascript, как правило, становятся довольно сложными. Если вы хотите использовать наследование, я бы рекомендовал typescript. Тем не менее, тонкие контроллеры-лучший способ пойти на мою точку зрения.