似乎没有办法向Angular控制器提供数据,除了通过指令处理的DOM中的属性(其中ngInit
是一个方便的例子)。
我想提供其他“构造函数”数据,例如具有我的功能的对象
$scope
。
背景:我们有一个现有的仪表板式单页面应用程序,
其中每个窗口小部件管理<div>
和特定于窗口小部件实例的数据
作为对象提供支持函数等。此对象数据
不适合DOM属性或ngInit
调用。
我无法想出一个比拥有全局哈希更好的方法,并使用特定于实例的唯一键。在调用angular.bootstrap(domElement, ['myApp'])
之前,我们在键下的这个全局哈希中设置所有“构造函数”参数,然后使用
<div ng-init='readInitialValuesFromHash("uniqueKey")'>...</div>
其中readInitialValuesFromHash
获取其所有数据
globalHash["uniqueKey"]
并在$scope
中存储它所需的内容(可能
只是"uniqueKey"
)。
(看似替代方案是使用指令和jQuery.data()
,但jQuery.data
uses a global hash behind the scenes)
当然我可以在函数中隐藏全局数据,但从根本上仍然可以使用 单例/全局变量。这个“全局哈希和传递密钥作为ng init的参数 诀窍“看起来就像这样的黑客......
我错过了什么吗?有没有更好的方法,鉴于 特定于widget实例的数据实际上比适合的更复杂 由于传统仪表板,通过指令/属性插入DOM 框架?
在$scope
中放置复杂对象时是否存在危险,只要它们不被指令,{{}}
或$scope.$watch()
调用引用?
根据需要将AngularJS添加到现有页面中
所以鉴于此,我该怎么办?
编辑:评论要求我更清楚地提问。作为一个非平凡的构造函数参数的示例,假设我想将此myObj
赋予控制器,原型继承,函数和所有:
var proto = {
p1: "p1",
pf: function() {
return "proto"
}
};
function MyObj(ost) {
this.ost = ost;
}
MyObj.prototype=proto;
var myObj = new MyObj("OST");
所以我有myObj,我有一个字符串:
<div ng-app='myApp' ng-controller="MyCtrl">....</div>
我把字符串放在DOM中,然后调用angular.bootstrap()
。
如何将myObj
MyCtrl
对象加入$scope
<div>
{{1}},而不是序列化/反序列化的版本/副本?
答案 0 :(得分:3)
服务是您正在寻找的。
您可以创建自己的服务,然后将它们指定为组件(控制器,指令,过滤器,服务)的依赖项,因此Angular的依赖注入将负责其余的工作。
需要牢记的要点:
服务是应用程序单例。这意味着每个注入器只有一个给定服务的实例。由于Angular对全局状态是致命的过敏,因此可以创建多个注入器,每个注入器都有自己的给定服务实例,但这很少需要,除了在此属性非常重要的测试中。
懒惰地实例化服务。这意味着只有在需要实例化服务或依赖于它的应用程序组件时才会创建服务。换句话说,除非应用程序直接或间接请求服务,否则Angular不会实例化服务。
服务(可通过DI注入)强烈倾向于全局状态(不是),因为它们更易于测试(例如易于模拟等)和“更安全”(例如防止意外冲突)。
相关链接:
<强> 实施例 强>
根据您的具体要求,最好创建一个服务来保存所有配置数据或为每个小部件创建一个服务。在后一种情况下,将所有服务包含在自己的模块中并将其指定为主模块的依赖项可能是个好主意。
var services = angular.module('myApp.services', []);
services.factory('widget01Srv', function () {
var service = {};
service.config = {...};
/* Other widget01-specific data could go here,
* e.g. functionality (but not presentation-related stuff) */
service.doSomeSuperCoolStuff = function (someValue) {
/* Send `someValue` to the server, receive data, process data */
return somePrettyInterestingStuff;
}
...
return service;
}
services.factory('widget02Srv', function () {...}
...
var app = angular.module('myApp', ['myApp.services']);
app.directive('widget01', function ('widget01Srv') {
return function postLink(scope, elem, attrs) {
attrs.$set(someKey, widget01Srv.config.someKey);
elem.bind('click', function () {
widget01Srv.doSomeSuperCoolStuff(elem.val());
});
...
};
});
答案 1 :(得分:0)
ExpertSystem的答案给了我一些我需要的提示。每个小部件的单独控制器实例。请注意constructorParameter
(== myObj
)如何插入控制器。
function creatWidgetInstance(name) {
....
var controllerName = name + 'Ctrl';
// myObj comes from the original question
var constructorParameter = myObj;
widgetApp.controller(controllerName, function($scope) {
$scope.string = constructorParameter;
});
....
newWidget = jQuery(...);
newWidget.attr('ng-controller', controllerName);
angular.bootstrap(newWidget[0], ['widgetApp']);
....
}
中查看它
也许一个更美观的解决方案是使用单独的服务,如:
function creatWidgetInstance(name) {
....
var controllerName = name + 'Ctrl';
var serviceName = name + 'Service';
// myObj comes from the original question
var constructorParameter = myObj;
widgetApp.factory(serviceName, function () {
return { savedConstructorParameter: constructorParameter };
});
widgetApp.controller(controllerName,
[ '$scope', serviceName, function($scope, service) {
$scope.string = service.savedConstructorParameter;
}
]
);
....
newWidget = jQuery(...);
newWidget.attr('ng-controller', controllerName);
angular.bootstrap(newWidget[0], ['widgetApp']);
....
}
在工作Plunker
中查看此内容答案 2 :(得分:0)
问题的答案需要回溯一些假设。我认为设置$scope
的唯一方法是在控制器上执行此操作。因此,问题围绕如何“向Angular控制器提供数据,而不是通过指令处理的DOM中的属性”。
这是错误的。
相反,人们可以这样做:
var scope = $rootScope.$new();
// Actually setting scope.string=scope makes for a horrible example,
// but it follows the terminology from the rest of the post.
scope.string = myObj;
var element = $compile(jQuery('#widgetTemplate').html())(scope);
jQuery('#widgets').append(element);
有关工作示例,请参阅此Plunker。