扩展AngularJS控制器的推荐方法是什么?

vla*_*ija 190 javascript dry angularjs

我有三个非常相似的控制器.我想要一个控制器,这三个扩展并共享其功能.

Enz*_*zey 301

也许没有扩展控制器,但可以扩展控制器或使单个控制器成为多个控制器的混合.

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.
}]);
Run Code Online (Sandbox Code Playgroud)

创建父控制器时,还会执行其中包含的逻辑.有关更多信息,请参阅$ controller(),但只$scope需要传递值.所有其他值将正常注入.

@mwarren,您的关注点由Angular依赖注入自动神奇地处理.您需要的只是注入$ 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);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

虽然在'complexController'创建$ document时,$ document没有传递给'simpleController',但是我们注入了$ document.

  • 我认为你不需要`$ .extend()`,你可以简单地调用`$ controller('CtrlImpl',{$ scope:$ scope});` (8认同)
  • @tomraithel不使用`angular.extend`(或`$ .extend`)实际上意味着只扩展`$ scope`,但如果你的基本控制器也定义了一些属性(例如`this.myVar = 5`),你只有使用`angular.extend`时,在扩展控制器中访问`this.myVar` (5认同)

Min*_*hev 52

对于继承,您可以使用标准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);
Run Code Online (Sandbox Code Playgroud)

如果您使用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);
Run Code Online (Sandbox Code Playgroud)

另一种方法是创建只是"抽象"构造函数,它将是你的基本控制器:

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

module.controller('ChildCtrl', ['$scope', function ($scope) {
  BaseController.call($scope);
  $scope.anotherClick = function () {
    //other actions
  };
}]);
Run Code Online (Sandbox Code Playgroud)

有关此主题的博客文章


And*_*ves 16

好吧,我不确定你想要实现什么,但通常服务是可行的.您还可以使用Angular的Scope继承特性在控制器之间共享代码:

<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
}
Run Code Online (Sandbox Code Playgroud)


Bar*_*art 15

您不扩展控制器.如果它们执行相同的基本功能,则需要将这些功能移动到服务中.该服务可以注入您的控制器.

  • @vladexologija这是我的意思的一个例子:http://jsfiddle.net/ERGU3/这是非常基本的,但你会明白的. (6认同)
  • 一个典型的例子是,当您在站点上有两个表单驱动的报表时,每个报表都依赖于大量相同的数据,并且每个报表都使用大量的共享服务.理论上你可以尝试将所有单独的服务放入一个带有几十个AJAX调用的大服务中,然后使用公共方法,如'getEverythingINeedForReport1'和'getEverythingINeedForReport2',并将它们全部设置为一个庞大的范围对象,但是你就是真正把控制器逻辑放在你的服务中.在某些情况下,扩展控制器绝对具有用例. (6认同)
  • @vladexologija我同意巴特在这里.我认为服务是混合的.您应尝试将尽可能多的逻辑移出控制器(进入服务).因此,如果您有3个控制器需要执行类似的任务,那么服务似乎是正确的方法.扩展控制器在Angular中并不自然. (4认同)
  • 谢谢,但我有4个功能已经使用服务(保存,删除等),并且在所有三个控制器中都存在相同的功能.如果延伸不是一种选择,是否有可能'mixin'? (3认同)
  • 没有任何论据和进一步解释,答案是毫无用处的.我也认为你有点想念OP的观点.已有共享服务.您唯一要做的就是直接公开该服务.我不知道这是不是一个好主意.如果需要访问范围,您的方法也会失败.但是根据你的推理,我会将范围显式地作为范围的属性公开给视图,以便它可以作为参数传递. (3认同)

Nik*_*rov 10

然而,从该采取的另一项很好的解决方案的文章:

// 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;
    });
}]);
Run Code Online (Sandbox Code Playgroud)


Rag*_*dra 7

您可以通过注入来创建服务并在任何控制器中继承其行为.

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

    var reusableCode = {};

    reusableCode.commonMethod = function() {
        alert('Hello, World!');
    };

    return reusableCode;
});
Run Code Online (Sandbox Code Playgroud)

然后在您想要从上面的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()

});
Run Code Online (Sandbox Code Playgroud)

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


kar*_*una 5

您可以尝试这样的事情(尚未测试):

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

app.controller('childController', baseController(function(){

}));
Run Code Online (Sandbox Code Playgroud)