从遗留代码调用 AngularJS

Call AngularJS from legacy code

本文关键字:调用 AngularJS 代码      更新时间:2023-09-26

我正在使用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 调整大小函数中,我想调度一个控制器可以听到的事件。似乎创建服务是要走的路。你能从AngularJS之外更新服务吗?控制器是否可以侦听来自服务的事件?在一个实验中(点击小提琴(,我似乎可以访问服务,但更新服务的数据不会反映在视图中(在示例中,应将<option>添加到<select>中(。

谢谢!

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

对于任何 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.
});

Misko 给出了正确的答案(显然(,但我们中的一些新手可能需要进一步简化它。

当涉及到从旧应用程序中调用 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);
}); 

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

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

多亏了上一篇文章,我可以使用异步事件更新我的模型。

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

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

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>

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

在此处查看演示。正如我在演示中所示,您可以在存储范围并从全局"scopes"对象中删除时触发 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('scopeDestroy'(。如果它不起作用,则在文档本身而不是元素上触发事件可能会有所帮助。( 请参阅应用程序.js ( 演示 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();配置我的应用程序如果我们更改范围,可能会破坏应用程序。 但是,如果你的应用了解范围,并且可以依赖它不经常更改,那么之前建议的是一种可行的方法。

希望这有帮助。 也欢迎任何反馈。