如何使用 TypeScript 静态注入器模式注入`$rootScope`?
Posted
技术标签:
【中文标题】如何使用 TypeScript 静态注入器模式注入`$rootScope`?【英文标题】:how do I inject `$rootScope` using TypeScript static injector pattern? 【发布时间】:2016-02-22 04:40:12 【问题描述】:所以,最后在获得一个大型模型以 Angular 和 typescript 进行编译之后,我得到了这个运行时错误:
10angular.js:12314 错误:[ng:cpws] 无法复制!制作副本 不支持 Window 或 Scope 实例。 http://errors.angularjs.org/1.4.1/ng/cpws
at angular.js:68
at copy (angular.js:857)
at copy (angular.js:932)
at copy (angular.js:888)
at Scope.$digest (angular.js:15666)
at Scope.$apply (angular.js:15935)
at bootstrapApply (angular.js:1624)
at Object.invoke (angular.js:4443)
at doBootstrap (angular.js:1622)
at bootstrap (angular.js:1642)
我没有更改任何“实现”,我只是重新构造了类以使用 TypeScript。在我的代码中没有任何地方可以使用 angular.copy(在整个应用程序的任何类中)。在多次将头撞在墙上之后,我偶然发现了这个问题。构造函数将 $rootScope 分配给一个局部变量(由于 TS,它现在位于原型上)。现在,这是一种旧代码,我只使用 $rootScope 的 ref 将其用作所有使用 $rootScope.broadcast(...)
的视图控制器的通用事件调度程序。当我使用标准角度样板注入它时,它曾经工作正常,我可以重构它......
/// <reference path="../../app/reference.ts" />
class UserModel
roles:string;
activeRole:string;
errorString:string;
loginAttemptEmailValue:string;
successString:string;
static $inject = ['utils', '$rootScope','$q', '$cookies', '$http','settings', '$location'];
constructor(u,r,q,c,h,s,l)
this.utils = u;
this.rootScope = r; // <---- the problem. comment-out this line and it works.
this.q = q;
this.cookies = c;
this.http = h;
this.settings = s;
this.location = l;
...
angular.module('App').service('userModel', UserModel );
但这让我担心,当我去转换我的控制器时,我会遇到同样的问题,只是注入 $scope。
所以,我的问题是,我如何使用这个 TypeScript 静态注入器模式正确注入 $scope
而不会出现这个可怕的复制错误?
我尝试创建一个类级别的静态作用域变量,但在尝试分配给静态类型时当然会出现左侧赋值错误。
更新: 这是转译的js。
var UserModel = (function ()
function UserModel(u, r, q, c, h, s, l)
var _this = this;
this.utils = u;
this.rootScope = r;
this.q = q;
this.cookies = c;
this.http = h;
this.settings = s;
this.location = l;
UserModel.prototype.hasActiveRole = function (val)
if (this.activeRole === val)
return true;
return false;
;
//...other implementation...
UserModel.$inject = ['utils', '$rootScope', '$q', '$cookies', '$http', 'settings', '$location'];
return UserModel;
)();
angular.module('App').service('userModel', UserModel);
【问题讨论】:
澄清请求:如果您注释掉this.rootScope = r;
,那么您的应用仍然可以正常运行(减去广播)?
是的!它是使应用程序立即出错的那一行。我可以将其注释掉,稍后在程序执行时得到一个正常的 null 错误。
有道理,你能分享使用$rootScope
的代码吗?你上面的作业是正确的。
对,我只用来广播。 this.rootScope.$broadcast('changeRole');
可能是另一个广播“loggedOut”之类的。我目前正在尝试将控制器转换为 TS,看看这实际上有多大意义。
【参考方案1】:
所以,如果其他人碰到同样的砖墙,请在这里回答我自己的问题。我相信这种情况可能是 Angular 错误或缺少关于将 $rootScope
分配给服务中的局部变量的文档。
注入$scope
或$rootScope
并分配给本地引用属性似乎工作正常,在控制器中,但不是服务。我还注意到您无法将$scope
导入服务,也许角度团队也打算阻止注入$rootScope
,但忽略了这样做。
错误本身令人费解;我最好的猜想是编译中存在一个工件(在 Angular 的内部深处),其中服务的实例化尝试深度复制 $rootScope
属性而不是浅复制当且仅当它被分配给服务对象上的属性时.
【讨论】:
【参考方案2】:有同样的问题。就是这个东西
at Scope.$digest (angular.js:15666)
您将 UserModel 作为服务传递给 Angular,并在每个公共属性上添加 Angular 手表,包括所有这些
this.utils = u;
this.rootScope = r;
this.q = q;
this.cookies = c;
this.http = h;
this.settings = s;
this.location = l;
所以当 $digest 开始时,它会尝试save value as last
watch.last = watch.eq ? copy(value, null) : value;
然后 copy() 发生 :) 错误 because of
if (isWindow(source) || isScope(source))
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
我使用存储类的 rootScope 的外部变量临时解决了这个问题
let rootScope: ng.IRootScopeService;
class Settings()
static $inject = [
'$rootScope'
];
constructor($rootScope: ng.IRootScopeService)
rootScope = $rootScope;
稍后将使用 RxJs Observables 替换 $rootScope 使用作为 pub/sub
【讨论】:
【参考方案3】:显然,您正试图深入观察您班级的整个实例。如果此对象的属性之一指向范围实例,则不应该这样做。如果您都想这样做,您可以尝试使用 getter 函数而不是普通属性:this.getRootScope = () => $rootScope
,这可能有效,但听起来仍然不是一个好主意。因为出于性能原因,当您深度观察某物时,您希望被观察的对象尽可能小。如果你只是想检测一些数据的变化,那么同时寻找注入服务的变化是否有意义?显然,您将错误的对象传递给 $watch
。
(顺便说一句,我建议使用 ngAnnotate 而不是手写static $inject...
。)
【讨论】:
没有手表。这只是将 $rootScope 注入服务的问题......除非有一些我不知道的自动绑定手表。 堆栈跟踪清楚地显示某处有一个手表,它正在跟踪整个userModel
对象。像这样:$scope.$watch(userModel, ..., true)
.
我看到了一个摘要,但没有手表。你指的是什么?
实际上,消费控制器的实现只关注一个属性,loginStatus。 this.scope.$watch(() => return this.userModel.loginStatus; , (oldStatus, newStatus ) => if( oldStatus !== newStatus ) this.handleLoginStatusChange(); );跨度>
Digest是手表加工的地方。 angular.copy
被深度手表用来存储被监视表达式值的副本。如果copy
是直接从$digest
调用的,则可能意味着仅这一件事。以上是关于如何使用 TypeScript 静态注入器模式注入`$rootScope`?的主要内容,如果未能解决你的问题,请参考以下文章
使用依赖注入取代硬连接资源(静态工厂单例),也可用于构造方法bulider模式
设计模式 - 如何仅在某些情况下强制执行对象属性(构建器模式,依赖注入)