【发布时间】:2015-03-09 12:53:38
【问题描述】:
如果父控制器注入了服务,控制器的子控制器是隐式继承服务还是必须显式注入服务?
【问题讨论】:
如果父控制器注入了服务,控制器的子控制器是隐式继承服务还是必须显式注入服务?
【问题讨论】:
您需要明确地注入服务。子控制器继承了作用域,但是服务当然不在作用域对象上(除非你把它放在那里)
查看这个 jsfiddle 来证明它:http://jsfiddle.net/HB7LU/11596/
var myApp = angular.module('myApp',[]);
myApp.factory('serviceId', function() {
var a = 1;
return {
a: a
};
});
myApp.controller('Ctrl',['$scope', 'serviceId' ,function($scope, serviceId) {
$scope.name = 'Superhero' + serviceId.a;
}])
myApp.controller('ChildCtrl',['$scope',function($scope) {
// the next line throws an undefined error because of serviceId
$scope.name = 'Superhero' + serviceId.a;
}])
【讨论】:
如果您进行原型继承,那么您可以从父控制器中获得一个字段,作为可以在派生控制器中使用的东西。为了使 Angular js 控制器可继承,您必须做一些事情。希望下面的解释会更清楚。
JavaScript 中的原型继承
所以首先让我们定义我们将如何从原型继承对象。在 javascript 中,原型继承可以按如下方式完成:
function Foo(some_param)
{
this.some_param = some_param
}
function Bar(some_param, some_specialized_param)
{
/**
* Call the super class constructor
*/
Foo.call(this, some_param);
this.some_specialized_param = some_specialized_param;
}
/**
* Now that you have defined the constructor, you need to
* define the prototypical inheritance
*/
Bar.prototype = Object.create(Foo.prototype);
/**
* Now you have to set the constructor
*/
Bar.prototype.constructor = Bar;
所以使用这个例子你可以创建 angularjs 控制器。然而有一个问题。例如,如果类 Bar 位于不同的模块或目录中,则需要导入定义。您可以使用其他工具(如 RequireJS)将模块/控制器/类定义加载到定义 Bar 的文件中,然后按如下方式继承。但是,您也可以使用 angularjs 结构,如下所示:
AngularJS 中的控制器继承
因此,您可以使用两个 angularjs 构造来定义控制器的实例化点(angular.module(..).constructor)和控制器的定义(angular.module(..).factory)。 AngularJS 工厂可用于控制器的类定义。
angular.module('some_module')
.factory('BaseCtrlClass', ['aService', function(aService)
{
function BaseCtrl($scope)
{
/**
* Assign a reference to "aService"
* which is injected whithin this factory
*/
this.aService = aService;
this.$scope = $scope;
}
return BaseCtrl;
}]);
现在您可以定义一个控制器定义,以便您可以使用 ngController 实例化它,例如:
angular.module('some_module')
.controller('BaseCtrl', [$scope, 'BaseCtrlClass', function($scope, BaseCtrlClass)
{
return new BaseCtrlClass($scope);
}]);
请注意,在工厂中,我已将其定义为 BaseCtrlClass,因为这是类定义。在控制器构造中,我定义了 BaseCtrl,您可以使用 ngController 在外部使用它。 BaseCtrlClass 名称应该在派生控制器内部使用,我们将在下面看到。因此,正如我提到的,当您尝试在另一个文件/模块中导入 BaseCtrlClass 的定义时,问题就出现了,理想情况下是定义 DerivedCtrl 类的文件。让我们定义 DerivedCtrl 类如下:
angular.module('a_different_module')
.factory('DerivedCtrlClass', ['BaseCtrlClass', function(BaseCtrlClass)
{
function DerivedCtrl($scope, aDifferentService)
{
BaseCtrlClass.call(this, $scope);
this.aDifferentService = aDifferentService //Specialized resource
}
DerivedCtrl.prototype = Object.create(BaseCtrlClass.prototype);
DerivedCtrl.prototype.constructor = DerivedCtrl;
return DerivedCtrl;
}])
.controller('DerivedCtrl', ['$scope', 'aDiferentService', 'DerivedCtrlClass', function($scope, aDifferentService, DerivedCtrlClass)
{
return new DerivedCtrlClass($scope, aDifferentService);
}]);
所以在这里你可以看到我已经在一个名为 DerivedCtrlClass 的工厂中定义了 DerivedCtrl。这实质上定义了控制器类定义。所以任何其他试图从它继承的模块或类都可以利用这个工厂。在构造函数中,我调用了 BaseCtrlClass 的构造函数,根据需要传递“this”和“$scope”作为参数。后来我定义了原型构造和默认构造函数,类似于我之前提到的 javascript 示例。为了方便起见,我还定义了一个控制器结构,它也可以与 ngController 一起使用。
我要强调的要点是 DerivedCtrl 的实例现在可以使用
this.aService
这是在 BaseCtrl 中定义的。基本上,在 BaseCtrl 和 DerivedCtrl 范围内定义的任何内容现在都可以在 DerivedCtrl 的实例中使用。当您尝试定义 DerivedCtrl 时,这在 html 中非常有效
<div ng-controller="DerivedCtrl">
......
</div>
范围继承
如果你想拥有范围继承,你可以使用这样的东西:
<div ng-controller="BaseCtrl">
<div ng-controller="DerivedCtrl">
...
</div>
</div>
这样做的问题是您需要使用 $scope 来传递字段。在 BaseCtrl 的构造函数中,您需要执行以下操作:
$scope.aService = aService
然后您可以从 DerivedCtrl 使用 $scope.aService。这不能很好地工作,因为它破坏了封装。为了可维护性,您可能不希望导出此服务并使其伪受保护,以便仅由继承自 BaseCtrl 的类访问。在那种情况下,我提供的解决方案效果很好,至少我一直在使用这种方式。
希望这会有所帮助。
【讨论】: