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

IT技术 javascript angularjs dry
2021-01-16 09:34:50

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

6个回答

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

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,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);
<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 在由 'complexController' 创建时没有传递到 'simpleController' 中,但为我们注入了 $document。

这很棒!只要确保记住扩展所有需要的功能,即我有类似的东西: handleSubmitClick它会调用handleLogin它反过来有一个loginSuccessloginFail所以,在我的扩展控制器然后我不得不超载handleSubmitClickhandleLogin以及loginSucess正确的loginSuccess功能使用。
2021-03-13 09:34:50
我认为你不需要那个$.extend(),你可以简单地打电话$controller('CtrlImpl', {$scope: $scope});
2021-03-15 09:34:50
完美,很棒的解决方案!
2021-03-31 09:34:50
@tomraithel 不使用angular.extend(或$.extend)实际上意味着扩展$scope唯一的,但是如果您的基本控制器还定义了一些属性(例如this.myVar=5),则this.myVar在使用时您只能访问扩展控制器angular.extend
2021-04-01 09:34:50
迄今为止,最快、最干净、最简单的解决方案!谢谢!
2021-04-06 09:34:50

对于继承,您可以使用标准的 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
  };
}]);

关于此主题的博客文章

好吧,我不确定您想要实现什么,但通常服务是要走的路。您还可以使用 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
}
最后,这似乎是最有角度的方式。我在三个不同的页面上有三个非常简短的 parentController,它们只是设置了 childController 可以选择的 $scope 值。childController,我原本想扩展的,包含了所有的控制器逻辑。
2021-03-20 09:34:50
在做类似事情的控制器之间共享相同的变量和函数(一个用于编辑,另一个用于创建等......)。这绝对是解决方案之一......
2021-03-21 09:34:50
不会$scope.$parent.fx( ) 是一个更干净的方法来做到这一点,因为那是它实际定义的地方?
2021-03-30 09:34:50
$scope 继承是迄今为止最好的方法,比公认的答案要好得多。
2021-04-06 09:34:50

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

@vladexologija 我同意 Bart 的观点。我认为服务是混合的。您应该尝试将尽可能多的逻辑移出控制器(进入服务)。因此,如果您有 3 个控制器需要执行类似的任务,那么服务似乎是正确的方法。在 Angular 中扩展控制器感觉不自然。
2021-03-25 09:34:50
@vladexologija 这是我的意思的一个例子:jsfiddle.net/ERGU3这是非常基本的,但你会明白的。
2021-04-07 09:34:50
谢谢,但我有 4 个功能已经使用服务(保存、删除等),并且在所有三个控制器中都存在。如果扩展不是一种选择,是否有可能使用 'mixin' ?
2021-04-08 09:34:50
没有任何论据和进一步解释,答案是毫无用处的。我也认为你有点错过了 OP 的观点。已经有一个共享服务。您唯一要做的就是直接公开该服务。我不知道这是否是个好主意。如果需要访问范围,您的方法也会失败。但是按照您的推理,我会将范围作为范围的属性显式公开给视图,以便它可以作为参数传递。
2021-04-10 09:34:50
一个典型的例子是,当您的站点上有两个表单驱动的报表时,每个报表都依赖于大量相同的数据,并且每个报表都使用大量共享服务。从理论上讲,您可以尝试将所有单独的服务放入一个包含数十个 AJAX 调用的大型服务中,然后使用诸如“getEverythingINeedForReport1”和“getEverythingINeedForReport2”之类的公共方法,并将其全部设置为一个庞大的范围对象,但随后您就真正将本质上是控制器逻辑的内容放入您的服务中。在某些情况下,扩展控制器绝对有用例。
2021-04-10 09:34:50

取自本文的另一个很好的解决方案

// 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;
    });
}]);
这对我来说非常有效。它的优势在于,您可以从一个控制器开始,创建另一个非常相似的控制器,然后想让代码变得干燥。无需更改代码,只需将其拉出即可。
2021-03-13 09:34:50