从遗留代码调用 AngularJS

我使用 AngularJS 来构建与遗留 Flex 应用程序交互的 HTML 控件。Flex 应用程序的所有回调必须附加到 DOM 窗口。

例如(在 AS3中)

ExternalInterface.call("save", data);

我会打电话的

window.save = function(data){
// want to update a service
// or dispatch an event here...
}

在 JS resize 函数中,我希望分派一个控制器可以听到的事件。似乎创建一个服务才是正确的选择。你能从 AngularJS 之外更新服务吗?控制器可以侦听来自服务的事件吗?在一个 实验(点击小提琴)中,我似乎可以访问一个服务,但是更新该服务的数据并没有反映在视图中(在这个例子中,<option>应该添加到 <select>中)。

谢谢!

91589 次浏览

互操作从外部的角度到角度是相同的调试角度应用程序或与第三方库集成。

对于任何 DOM 元素,您都可以这样做:

  • angular.element(domElement).scope()获取元素的当前范围
  • angular.element(domElement).injector()获取当前应用程序注入器
  • angular.element(domElement).controller()获取 ng-controller实例。

从注射器,你可以得到任何服务的角度应用。类似地,您可以从作用域调用发布到它的任何方法。

请记住,对角模型的任何更改或对作用域的任何方法调用都需要像下面这样包装在 $apply()中:

$scope.$apply(function(){
// perform any model changes or method invocations here on angular app.
});

感谢上一篇文章,我可以用一个异步事件更新我的模型。

<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;
});
};

我所发现的概念的最佳解释在这里: 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);
});

Misko 给出了正确的答案(很明显) ,但是我们这些新手可能需要进一步简化。

如果要在遗留应用程序中调用 AngularJS 代码,可以将 AngularJS 代码想象成存在于遗留应用程序中受保护容器中的“微应用程序”。您不能直接对它进行调用(理由很充分) ,但是您可以通过 $scope 对象进行远程调用。

要使用 $scope 对象,需要获取 $scope 的句柄。

您可以使用 AngularJS“ micro- app”HTML 中任何 HTML 元素的 id 来获取 AngularJS app $scope 的句柄。

例如,假设我们想在 AngularJS 控制器中调用一些函数,比如 say Hi ()和 say bye ()。在 AngularJSHTML (视图)中,我们有一个 div,ID 为“ MySuperAwesomeApp”。您可以使用以下代码,结合 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

进一步的其他答案。 如果你不想访问控制器中的方法,但是想直接访问服务,你可以这样做:

// 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

更安全和高性能的方法,尤其是当调试数据关闭时,是使用共享变量来保存回调函数。角度控制器实现这个函数,将其内部结构返回给外部代码。

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;
}
}]);

作为可重用指令的一般化:

我创建了一个“ exposeScope”指令,它以类似的方式工作,但用法更简单:

<div ng-controller="myController" expose-scope="aVariableNameForThisScope">
<span expose-scope='anotherVariableNameForTheSameScope" />
</div>

这将当前范围(给定指令的链接函数)存储在一个全局“ scope”对象中,该对象是所有范围的持有者。提供给指令属性的值用作此全局对象中范围的属性名。

查看演示 给你。正如我在演示中所展示的,当范围被存储并从全局“ scope”对象中删除时,您可以触发 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 中删除时,我还没有测试 on (‘ scopeDestroied’)。如果不起作用,在文档本身而不是元素上触发事件可能会有所帮助。(参见 app.js)脚本中的 demo plunker。

我知道这是一个老问题,但我最近正在寻找这样做的选项,所以我想我把我的发现放在这里,以防它对任何人都有用。

在大多数情况下,如果需要外部遗留代码与 UI 的状态或应用程序的内部工作进行交互,那么服务可能有助于抽象出这些更改。如果外部代码直接与角度控制器、组件或指令进行交互,那么您的应用程序将与遗留代码严重耦合,这是一个坏消息。

在我的案例中,我最终使用的是浏览器可访问的全局变量(例如窗口)和事件处理的组合。我的代码有一个智能表单生成引擎,它需要 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.module('myApp.services').
service('FormSchemaService', ['$window' , FormSchemaService ])

在我的手柄里: 函数(){ 「严格使用」 ;

angular.module('myApp').controller('MyController', MyController);


MyEncapsulatorController.$inject = ['$scope', 'FormSchemaService'];


function MyController($scope, formSchemaService) {
// using the already configured formSchemaService
formSchemaService.buildForm();

到目前为止,这是纯粹的面向角度和 javascript 服务的编程:

<script type="text/javascript">


(function(app){
var conf = app.conf = {
'fields': {
'field1: { // field configuration }
}
} ;


app.dispatchEvent(new Event('register-schema'));


})(window);
</script>

显然,每种方法都有其优缺点。这种方法的优点和使用取决于您的 UI。前面建议的方法在我的情况下不起作用,因为我的表单模式和遗留代码没有角度范围的控制和知识。因此,我的应用程序配置基于 angular.element('element-X').scope(); 如果我们改变范围,可能会破坏应用程序。但是,如果你的应用程序有知识的范围,可以依赖它不经常改变,以前的建议是一个可行的方法。

希望这有所帮助。任何反馈也是受欢迎的。