从旧代码中调用AngularJS
我正在使用AngularJS构build与旧版Flex应用程序交互的HTML控件。 Flex应用程序的所有callback都必须附加到DOM窗口。
例如(在AS3中)
ExternalInterface.call("save", data);
将会通知
window.save = function(data){ // want to update a service // or dispatch an event here... }
从JS resize函数中,我想派发一个控制器可以听到的事件。 看来创build一个服务是一条路。 你可以更新AngularJS以外的服务吗? 控制器可以监听来自服务的事件吗? 在一个实验中(点击小提琴)我似乎可以访问一个服务,但更新服务的数据并没有反映在视图(在这个例子中<option>
应该被添加到<select>
)。
谢谢!
从angular度外的Interop与debuggingangular度的应用相同或与第三方库集成。
对于任何DOM元素,你可以这样做:
-
angular.element(domElement).scope()
获取元素的当前作用域 -
angular.element(domElement).injector()
获取当前的应用程序注入器 -
angular.element(domElement).controller()
获取ng-controller
实例的持有者。
从注射器,您可以获得任何angular度应用服务。 与范围类似,您可以调用已经发布给它的任何方法。
请记住,angular度模型或作用域上的任何方法调用的任何更改都需要像这样包装在$apply()
:
$scope.$apply(function(){ // perform any model changes or method invocations here on angular app. });
米斯科给出了正确的答案(显然),但是我们中的一些新手可能需要进一步简化。
当从传统应用程序中调用AngularJS代码时,可以将AngularJS代码看作是遗留应用程序中受保护容器内的“微应用程序”。 你不能直接调用它(出于很好的理由),但你可以通过$ scope对象进行远程调用。
要使用$ scope对象,您需要获取$ scope的句柄。 幸运的是,这很容易做到。
您可以使用您的AngularJS“微应用”HTML中的任何HTML元素的id来获取AngularJS应用$ scope的句柄。
作为一个例子,假设我们想在AngularJS控制器中调用一些函数,如sayHi()和sayBye()。 在AngularJS HTML(视图)中,我们有一个ID为“MySuperAwesomeApp”的div。 你可以使用下面的代码,结合jQuery来获取$ scope的句柄:
var microappscope = angular.element($("#MySuperAwesomeApp")).scope();
现在你可以通过范围句柄来调用你的AngularJS代码函数:
// we are in legacy code land here... microappscope.sayHi(); microappscope.sayBye();
为了使事情更方便,你可以使用一个函数来随时抓取范围句柄来访问它:
function microappscope(){ return angular.element($("#MySuperAwesomeApp")).scope(); }
你的电话会看起来像这样:
microappscope().sayHi(); microappscope().sayBye();
你可以在这里看到一个工作的例子:
http://jsfiddle.net/peterdrinnan/2nPnB/16/
我还在渥太华AngularJS组的幻灯片中展示了这一点(只是跳到最后2张幻灯片)
http://www.slideshare.net/peterdrinnan/angular-for-legacyapps
我发现的概念的最大的解释在这里: https : //groups.google.com/forum/#! msg/angular/ kqFrwiysgpA/eB9mNbQzcHwJ
为了节省您的点击:
// get Angular scope from the known DOM element e = document.getElementById('myAngularApp'); scope = angular.element(e).scope(); // update the model with a wrap in $apply(fn) which will refresh the view for us scope.$apply(function() { scope.controllerMethod(val); });
感谢上一篇文章,我可以用asynchronous事件更新我的模型。
<div id="control-panel" ng-controller="Filters"> <ul> <li ng-repeat="filter in filters"> <button type="submit" value="" class="filter_btn">{{filter.name}}</button> </li> </ul> </div>
我宣布我的模型
function Filters($scope) { $scope.filters = []; }
而且我从我的范围之外更新我的模型
ws.onmessage = function (evt) { dictt = JSON.parse(evt.data); angular.element(document.getElementById('control-panel')).scope().$apply(function(scope){ scope.filters = dictt.filters; }); };
进一步的其他答案。 如果您不想访问控制器中的某个方法,但想要直接访问该服务,则可以这样做:
// Angular code* : var myService = function(){ this.my_number = 9; } angular.module('myApp').service('myService', myService); // External Legacy Code: var external_access_to_my_service = angular.element('body').injector().get('myService'); var my_number = external_access_to_my_service.my_number
特别是当debugging数据closures时,更安全和高效的方法是使用共享variables来保存callback函数。 您的angular度控制器实现此function将其内部返回到外部代码。
var sharedVar = {} myModule.constant('mySharedVar', sharedVar) mymodule.controller('MyCtrl', [ '$scope','mySharedVar', function( $scope, mySharedVar) { var scopeToReturn = $scope; $scope.$on('$destroy', function() { scopeToReturn = null; }); mySharedVar.accessScope = function() { return scopeToReturn; } }]);
概括为一个可重用的指令:
我创build了一个以类似的方式工作的'exposeScope'指令,但用法更简单:
<div ng-controller="myController" expose-scope="aVariableNameForThisScope"> <span expose-scope='anotherVariableNameForTheSameScope" /> </div>
这将当前作用域(指向该指令的链接函数)存储在作为所有作用域的持有者的全局“作用域”对象中。 提供给指令属性的值用作此全局对象中作用域的属性名称。
在这里看到演示。 正如我在演示中展示的那样,当范围从全局“范围”对象中存储和删除时,可以触发jQuery事件。
<script type="text/javascript" > $('div').on('scopeLinked', function(e, scopeName, scope, allScopes) { // access the scope variable or the given name or the global scopes object }.on('scopeDestroyed', function(e, scopeName, scope, allScopes) { // access the scope variable or the given name or the global scopes object } </script>
请注意,当从DOM中删除实际元素时,我还没有testing过on('scopeDestroyed')。 如果它不起作用,触发文档本身而不是元素的事件可能会有所帮助。 (请参阅app.js)演示程序中的脚本。
我知道这是一个古老的问题,但我最近正在考虑select这样做,所以我想我把我的发现放在这里,以防万一对任何人有用。
在大多数情况下,如果需要使用外部遗留代码来与UI的状态或应用程序的内部工作进行交互,则可以使用服务来抽象化这些更改。 如果外部代码直接与您的angular度控制器,组件或指令交互,那么您将您的应用与您的遗留代码大量耦合,这是一个坏消息。
我最终使用的是一个浏览器可访问的全局variables(即窗口)和事件处理的组合。 我的代码有一个智能的表单生成引擎,它需要CMS的JSON输出来启动表单。 这是我所做的:
function FormSchemaService(DOM) { var conf = DOM.conf; // This event is the point of integration from Legacy Code DOM.addEventListener('register-schema', function (e) { registerSchema(DOM.conf); }, false); // service logic continues ....
表单模式服务是按照预期使用angular形注入器创build的:
angular.module('myApp.services'). service('FormSchemaService', ['$window' , FormSchemaService ])
在我的控制器中:function(){'use strict';
angular.module('myApp').controller('MyController', MyController); MyEncapsulatorController.$inject = ['$scope', 'FormSchemaService']; function MyController($scope, formSchemaService) { // using the already configured formSchemaService formSchemaService.buildForm();
到目前为止,这是纯粹的angular度和JavaScript面向服务的编程。 但遗留的整合来到这里:
<script type="text/javascript"> (function(app){ var conf = app.conf = { 'fields': { 'field1: { // field configuration } } } ; app.dispatchEvent(new Event('register-schema')); })(window); </script>
显然,每种方法都有其优点和缺点。 这种方法的优点和用途取决于您的用户界面。 以前build议的方法不适用于我的情况,因为我的表单模式和遗留代码没有angular度范围的控制和知识。 因此,根据angular.element('element-X').scope();
configuration我的应用程序angular.element('element-X').scope();
如果我们改变范围,可能会破坏应用程序。 但是,如果你的应用程序已经了解了范围界定的知识,并且可以依靠它而不是经常改变,那么以前的build议是一个可行的方法。
希望这可以帮助。 任何反馈也是受欢迎的。