从遗留代码中调用AngularJS

kre*_*eek 180 javascript actionscript-3 externalinterface angularjs

我正在使用AngularJS来构建与传统Flex应用程序交互的HTML控件.Flex应用程序中的所有回调都必须附加到DOM窗口.

例如(在AS3中)

ExternalInterface.call("save", data);
Run Code Online (Sandbox Code Playgroud)

将会通知

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

在JS resize函数中,我想发送一个控制器可以听到的事件.似乎创建服务是最佳选择.你能从AngularJS外部更新服务吗?控制器可以监听服务中的事件吗?在一个实验(点击小提琴)我做了似乎我可以访问服务,但更新服务的数据不会反映在视图中(在示例中<option>应该添加到<select>).

谢谢!

Mis*_*ery 292

从角度到角度之间的互操作与调试角度应用或与第三方库集成相同.

对于任何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.
});
Run Code Online (Sandbox Code Playgroud)

  • 我不能得到这个工作.我打电话`angular.element(的document.getElementById(divName))范围()',但我不能够从它调用任何功能,它只是返回"未定义"在控制台. (5认同)
  • 如果关闭调试数据,则scope().scope()将不起作用,这是生产的建议.在这种情况下,这不会使它无用吗?这仅用于测试/调试. (5认同)
  • 你不会想在Angular 1.3中这样做.Angular团队并不打算让我们在生产代码中的元素上调用".scope()".它本来是一个调试工具.因此,从Angular 1.3开始,您可以将其关闭.Angular将使用jQuery的.data函数停止将范围附加到元素.这将加快您的应用程序.此外,将范围切换到jquery的缓存功能将导致内存泄漏.所以,你绝对应该关闭它,以加快你的应用程序.Angular的网站有一个生产指南,您应该用它来了解更多信息. (4认同)

Pet*_*nan 86

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

如果从遗留应用程序中调用AngularJS代码,请将AngularJS代码视为遗留应用程序中受保护容器中存在的"微应用程序".你不能直接调用它(非常好的理由),但你可以通过$ scope对象进行远程调用.

要使用$ scope对象,需要获取$ scope的句柄.幸运的是,这很容易做到.

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

举个例子,假设我们想在AngularJS控制器中调用几个函数,比如sayHi()和sayBye().在AngularJS HTML(视图)中,我们有一个id为"MySuperAwesomeApp"的div.您可以使用以下代码,结合jQuery来获取$ scope的句柄:

var microappscope = angular.element($("#MySuperAwesomeApp")).scope();
Run Code Online (Sandbox Code Playgroud)

现在,您可以通过范围句柄调用AngularJS代码函数:

// we are in legacy code land here...

microappscope.sayHi();

microappscope.sayBye();
Run Code Online (Sandbox Code Playgroud)

为了使事情更方便,您可以使用函数在您想要访问它时随时获取范围句柄:

function microappscope(){

    return angular.element($("#MySuperAwesomeApp")).scope();

}
Run Code Online (Sandbox Code Playgroud)

您的电话将如下所示:

microappscope().sayHi();

microappscope().sayBye();
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到一个有效的例子:

http://jsfiddle.net/peterdrinnan/2nPnB/16/

我还在渥太华AngularJS小组的幻灯片中展示了这一点(只是跳到最后2张幻灯片)

http://www.slideshare.net/peterdrinnan/angular-for-legacyapps

  • 请注意,不建议使用仅链接答案,因此,答案应该是搜索解决方案的终点(相对于引用的另一个中途停留,随着时间推移会逐渐变得陈旧).请考虑在此处添加独立的概要,并将链接作为参考. (4认同)

Wis*_*man 24

我发现的概念的最佳解释位于: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);
}); 
Run Code Online (Sandbox Code Playgroud)

  • 当app和controller在同一元素中共存时,上述方法有效.对于使用ng-view指令到模板的更复杂的应用程序,您必须获取视图中的第一个元素,而不是整个应用程序的DOM元素.我不得不使用document.getElementsByClassName('ng-scope')来搜索元素; 节点列表,找出要抓取的正确范围DOM元素. (14认同)

Gui*_*ent 13

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

<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>
Run Code Online (Sandbox Code Playgroud)

我宣布我的模特

function Filters($scope) {
    $scope.filters = [];
}
Run Code Online (Sandbox Code Playgroud)

我从我的范围之外更新我的模型

ws.onmessage = function (evt) {
    dictt = JSON.parse(evt.data);
    angular.element(document.getElementById('control-panel')).scope().$apply(function(scope){
        scope.filters = dictt.filters;
    });
};
Run Code Online (Sandbox Code Playgroud)


Ale*_*itt 13

进一步回答其他问题.如果您不想访问控制器中的方法但想直接访问该服务,则可以执行以下操作:

// 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 
Run Code Online (Sandbox Code Playgroud)


Cag*_*lan 6

更安全和高效的方式,尤其是在调试数据关闭时,是使用共享变量来保存回调函数.角度控制器实现此功能以将其内部结构返回到外部代码.

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;
}
}]);
Run Code Online (Sandbox Code Playgroud)

广义为可重用指令:

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

<div ng-controller="myController" expose-scope="aVariableNameForThisScope">
   <span expose-scope='anotherVariableNameForTheSameScope" />
</div>
Run Code Online (Sandbox Code Playgroud)

这将当前作用域(指令的链接函数)存储在全局"作用域"对象中,该对象是所有作用域的持有者.提供给directive属性的值用作此全局对象中作用域的属性名称.

请在此处查看演示.正如我在演示中所展示的那样,当存储范围并从全局"范围"对象中删除时,您可以触发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>
Run Code Online (Sandbox Code Playgroud)

请注意,当从DOM中删除实际元素时,我还没有测试过on('scopeDestroyed').如果它不起作用,则在文档本身而不是元​​素上触发事件可能会有所帮助.演示plunker中的(请参阅app.js)脚本.