我想在Angular中实现原型继承,其中base类型被定义为Angular值。问题是设置我的孩子类型原型。假设这个简化的例子:
angular.module("Test")
.value("BaseController", BaseController);
BaseController.$inject = [...];
function BaseController(...) {
...
}
BaseController.prototype.func = function () {
...
};
angular.module("Test")
.controller("ChildController", ChildController);
ChildController.$inject = ["BaseController", ...];
function ChildController(BaseController, ...) {
BaseController.call(this, ...);
// save reference for later
ChildController.base = BaseController;
...
}
// ERROR - Clearly why
ChildController.prototype = Object.create(BaseController.prototype);
// ERROR - instance properties aren't available on this level
ChildController.prototype = Object.create(ChildController.base.prototype);
问题是在构造函数被实例化之前正在生成原型。但是在构造函数被实例化之前,我没有可能引用角度注入的BaseController
。
我能看到解决这个问题的唯一方法就是让我的BaseController
公开定义,这样我甚至可以在角度注入构造函数之前访问它。我不喜欢这个,因为我不能在函数闭包中使用我的代码 private ,我也希望尽可能多地使用angular的功能,而不需要使用常见的Javascript来对抗Angular代码。< / p>
有没有什么方法可以通过将基类型定义为角度值(或其他)来使原型继承工作?
答案 0 :(得分:1)
此解决方案专门针对您的方法。您可以使用模块的run
块来分配原型。在文件2中添加以下内容:
angular.module("Test").run(function(BaseController) {
ChildController.prototype = Object.create(BaseController.prototype);
});
BaseController
被注入并可用于创建原型。由于此代码在任何控制器实例化之前运行,因此您将获得原型继承。
另请注意,ChildController.$inject
必须包含所有BaseController.$inject
。
另一种方法是将BaseController
附加到模块本身:
angular.module("Test").BaseController = BaseController;
...
ChildController.prototype = Object.create(angular.module("Test").BaseController.prototype);
代码仍然是私有的,构造函数仍然只能通过模块访问。
您还可以寻找继承的替代方案。根据具体情况,分层控制器可能是一个可行的解决方案。
<div ng-controller="BaseController"><%-- Handle Generic stuff --%>
...
<div ng-controller="ChildController"><%-- Handle specific stuff --%>
答案 1 :(得分:0)
重要信息
- 而进一步扩展它
此解决方案专门针对与Angular相关的代码实现,该代码使用实际的Javascript原型来定义控制器/服务,而不仅仅是匿名函数,如Web上的日常示例所示 - 这意味着遵循{{3因为他没有使用真正的原型
- 醇>
所有构造函数必须使用
可以解决这个限制$inject
静态属性使用显式DI注释。
- 使用Angular injector
annotate
(如果内联提供注释 - 数组)或- 更改
.inherits
签名,以正确的顺序包含所有基本类型的构造函数参数作为其自己的参数,即
Child.inherits(Base, baseInjection1, baseInjection2, ...)
我已经提出了一种相当简单且最常用的方法来设置我的Angular控制器的类型继承。那么这种继承甚至可以超越它,并且可以与任何使用类型构造函数(控制器,服务等)的Angular资产一起使用。
生成的解决方案会更改原始文件&#39;内容以这种极其简单的形式出现:
angular.module("Test")
.value("BaseController", BaseController);
BaseController.$inject = [...];
function BaseController(...) {
...
}
BaseController.prototype.func = function () {
...
};
angular.module("Test")
.controller("ChildController", ChildController);
ChildController.$inject = ["BaseController", ...];
function ChildController(BaseController, ...) {
// ALL MAGIC IS HERE!
ChildController.inherits(BaseController, arguments);
...
}
所以我们要做的就是在我们的子类型构造函数中调用一个并提供基类型(由Angular DI注入到构造函数中)和子类型的构造函数参数(因此继承可以在运行基类型的构造函数时使用它们。)
.inherit
功能为了使事情变得通用我已将此函数添加到Function.prototype
对象,因此它可用于所有函数(或更好的构造函数)。这就是它的实施方式:
Function.prototype.inherits = function inherits(BaseType, constructorInjections) {
/// <summary>Sets type's inheritance to base type.</summary>
/// <param name="BaseType" type="Function" optional="false">Base type to set for this child type.</param>
/// <param name="constructorInjections" type="Array" optional="true">Child type constructor injection instances.</param>
// return if both angular types don't use explicit DI
if (!this.$inject || !BaseType.$inject) return;
// DRY
if (this.prototype.__proto__ === BaseType.prototype || Object.getPrototypeOf(this.prototype) === BaseType.prototype) return;
// #region construct base object instance
// make a quick-search dictionary of child constructor injections
for (var i = 0, l = this.$inject.length, dic = {}; i < l; i++)
{
dic[this.$inject[i]] = i;
}
// create base type's constructor injections array
for (var i = 0, l = BaseType.$inject.length, baseParams = []; i < l; i++)
{
baseParams.push(constructorInjections[dic[BaseType.$inject[i]]]);
}
// get base type's constructed instance
var baseInstance = BaseType.apply(baseInstance = {}, baseParams) || baseInstance;
// #endregion
// #region set type inheritance chain
if (Object.setPrototypeOf)
{
Object.setPrototypeOf(this.prototype, BaseType.prototype);
}
else
{
// doesn't do the same thing, but it should work just as well
angular.extend(this.prototype, BaseType.prototype, { __proto__: BaseType.prototype });
}
// #endregion
// #region add base class generated instance to prototype
for (var i = 0, keys = Object.keys(baseInstance), l = keys.length; i < l; i++)
{
this.prototype[keys[i]] = baseInstance[keys[i]];
}
// #endregion
};