Javascript 从遗留代码调用 AngularJS
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10490570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Call AngularJS from legacy code
提问by kreek
I'm using AngularJS to build HTML controls that interact with a legacy Flex application. All callbacks from the Flex app must be attached to the DOM window.
我正在使用 AngularJS 构建与旧版 Flex 应用程序交互的 HTML 控件。来自 Flex 应用程序的所有回调都必须附加到 DOM 窗口。
For example (in AS3)
例如(在 AS3 中)
ExternalInterface.call("save", data);
Will call
将会通知
window.save = function(data){
// want to update a service
// or dispatch an event here...
}
From within the JS resize function I'd like to dispatch an event that a controller can hear. It seems that creating a service is the way to go. Can you update a service from outside of AngularJS? Can a controller listen for events from a service? In one experiment (click for fiddle)I did it seems like I can access a service but updating the service's data doesn't get reflected in the view (in the example an <option>
should be added to the <select>
).
在 JS 调整大小函数中,我想发送一个控制器可以听到的事件。似乎创建服务是要走的路。您可以从 AngularJS 之外更新服务吗?控制器可以监听来自服务的事件吗?在一个实验中(单击小提琴),我似乎可以访问服务,但更新服务的数据并没有反映在视图中(在示例中<option>
应该添加到<select>
)。
Thanks!
谢谢!
回答by Misko Hevery
Interop from outside of angular to angular is same as debugging angular application or integrating with third party library.
从 angular 外部到 angular 的互操作与调试 angular 应用程序或与第三方库集成相同。
For any DOM element you can do this:
对于任何 DOM 元素,您都可以这样做:
angular.element(domElement).scope()
to get the current scope for the elementangular.element(domElement).injector()
to get the current app injectorangular.element(domElement).controller()
to get a hold of theng-controller
instance.
angular.element(domElement).scope()
获取元素的当前范围angular.element(domElement).injector()
获取当前的应用程序注入器angular.element(domElement).controller()
获取ng-controller
实例。
From the injector you can get a hold of any service in angular application. Similarly from the scope you can invoke any methods which have been published to it.
从注入器中,您可以获得 Angular 应用程序中的任何服务。类似地,您可以从范围调用任何已发布到它的方法。
Keep in mind that any changes to the angular model or any method invocations on the scope need to be wrapped in $apply()
like this:
请记住,对 angular 模型的任何更改或作用域上的任何方法调用都需要$apply()
像这样包装:
$scope.$apply(function(){
// perform any model changes or method invocations here on angular app.
});
回答by Peter Drinnan
Misko gave the correct answer (obviously), but some of us newbies may need it simplified further.
Misko 给出了正确的答案(显然),但我们中的一些新手可能需要进一步简化它。
When if comes to calling AngularJS code from within legacy apps, think of the AngularJS code as a "micro app" existing within a protected container in your legacy application. You cannot make calls to it directly (for very good reason), but you can make remote calls by way of the $scope object.
如果要从遗留应用程序中调用 AngularJS 代码,请将 AngularJS 代码视为存在于遗留应用程序中受保护容器中的“微型应用程序”。你不能直接调用它(有很好的理由),但你可以通过 $scope 对象进行远程调用。
To use the $scope object, you need to get the handle of $scope. Fortunately this is very easy to do.
要使用 $scope 对象,您需要获取 $scope 的句柄。幸运的是,这很容易做到。
You can use the id of any HTML element within your AngularJS "micro-app" HTML to get the handle of the AngularJS app $scope.
您可以使用 AngularJS“微应用”HTML 中任何 HTML 元素的 id 来获取 AngularJS 应用 $scope 的句柄。
As an example, let's say we want to call a couple of functions within our AngularJS controller such as sayHi() and sayBye(). In the AngularJS HTML (view) we have a div with the id "MySuperAwesomeApp". You can use the following code, combined with jQuery to get the handle of $scope:
例如,假设我们想在 AngularJS 控制器中调用几个函数,例如 sayHi() 和 sayBye()。在 AngularJS HTML(视图)中,我们有一个 ID 为“MySuperAwesomeApp”的 div。可以使用如下代码,结合jQuery来获取$scope的句柄:
var microappscope = angular.element($("#MySuperAwesomeApp")).scope();
Now you can call your AngularJS code functions by way of the scope handle:
现在你可以通过作用域句柄调用你的 AngularJS 代码函数:
// we are in legacy code land here...
microappscope.sayHi();
microappscope.sayBye();
To make things more convenient, you can use a function to grab the scope handle anytime you want to access it:
为了使事情更方便,您可以使用一个函数来随时访问范围句柄:
function microappscope(){
return angular.element($("#MySuperAwesomeApp")).scope();
}
Your calls would then look like this:
您的电话将如下所示:
microappscope().sayHi();
microappscope().sayBye();
You can see a working example here:
你可以在这里看到一个工作示例:
http://jsfiddle.net/peterdrinnan/2nPnB/16/
http://jsfiddle.net/peterdrinnan/2nPnB/16/
I also showed this in a slideshow for the Ottawa AngularJS group (just skip to the last 2 slides)
我还在 Ottawa AngularJS 小组的幻灯片中展示了这一点(直接跳到最后 2 张幻灯片)
http://www.slideshare.net/peterdrinnan/angular-for-legacyapps
http://www.slideshare.net/peterdrinnan/angular-for-legacyapps
回答by Wiseman
Greatest explanation of the concept I've found is situated here: https://groups.google.com/forum/#!msg/angular/kqFrwiysgpA/eB9mNbQzcHwJ
我发现的概念的最佳解释位于:https: //groups.google.com/forum/#!msg/angular/ kqFrwiysgpA/eB9mNbQzcHwJ
To save you the clicking:
为了节省您的点击:
// 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);
});
回答by Guillaume Vincent
Thanks to the previous post, I can update my model with an asynchronous event.
感谢上一篇文章,我可以使用异步事件更新我的模型。
<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>
I declare my model
我声明我的模型
function Filters($scope) {
$scope.filters = [];
}
And i update my model from outside my scope
我从我的范围之外更新我的模型
ws.onmessage = function (evt) {
dictt = JSON.parse(evt.data);
angular.element(document.getElementById('control-panel')).scope().$apply(function(scope){
scope.filters = dictt.filters;
});
};
回答by Alec Hewitt
Further to the other answers. If you don't want to access a method in a controller but want to access the service directly you can do something like this:
进一步的其他答案。如果您不想访问控制器中的方法但想直接访问服务,您可以执行以下操作:
// 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
回答by Cagatay Kalan
More safe and performant way especially when debug data is off is to use a shared variable to hold a callback function. Your angular controller implements this function to return its internals to the external code.
更安全和高效的方法是使用共享变量来保存回调函数,尤其是在调试数据关闭时。您的 angular 控制器实现了这个函数以将其内部结构返回给外部代码。
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;
}
}]);
Generalized as a reusable directive :
概括为可重用指令:
I created a 'exposeScope' directive which works in a similar fashion but usage is simpler:
我创建了一个“exposeScope”指令,它以类似的方式工作,但用法更简单:
<div ng-controller="myController" expose-scope="aVariableNameForThisScope">
<span expose-scope='anotherVariableNameForTheSameScope" />
</div>
This stores the current scope ( that is given to the link function of the directive) in a global 'scopes' object which is a holder for all scopes. Value provided to the directive attribute is used as the property name of the scope in this global object.
这将当前范围(提供给指令的链接函数)存储在全局“范围”对象中,该对象是所有范围的持有者。提供给指令属性的值用作此全局对象中范围的属性名称。
See the demo here. As I showed in the demo, you can trigger jQuery events when the scope is stored and removed from the global 'scopes' object.
请参阅此处的演示。正如我在演示中所展示的,当范围被存储并从全局“范围”对象中删除时,您可以触发 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>
Note that, I haven't tested the on('scopeDestroyed') when the actual element is removed from the DOM. If it does not work, triggering the event on the document itself instead of the element may help. ( see the app.js ) script in the demo plunker.
请注意,当实际元素从 DOM 中删除时,我还没有测试 on('scopeDestroyed') 。如果它不起作用,在文档本身而不是元素上触发事件可能会有所帮助。(参见 app.js )演示 plunker 中的脚本。
回答by Shakus
I know this is an old question but I was looking at options to do this recently, so I thought I put my findings here in case it's useful to anyone.
我知道这是一个老问题,但我最近正在寻找这样做的选项,所以我想我把我的发现放在这里,以防它对任何人有用。
In most cases, if there's the need for external legacy code to interact with the state of the UI or the inner workings of the application a service could be useful to abstract away those changes. If an external code is interacting directly with your angular controller, component or directive, you're coupling your app heavily with your legacy code which is bad news.
在大多数情况下,如果需要外部遗留代码与 UI 的状态或应用程序的内部工作进行交互,则服务可能有助于抽象出这些更改。如果外部代码直接与您的 Angular 控制器、组件或指令交互,则您的应用程序与遗留代码严重耦合,这是个坏消息。
What I ended up using in my case, is a combination of browser accessible globals (i.e. window ) and event handling. My code has a smart form generation engine which requires JSON output from a CMS to initiliase the forms. Here's what I have done :
我最终在我的案例中使用的是浏览器可访问全局变量(即 window )和事件处理的组合。我的代码有一个智能表单生成引擎,它需要来自 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 ....
Form Schema Service is created using angular injector as expected:
表单架构服务是按预期使用角度注入器创建的:
angular.module('myApp.services').
service('FormSchemaService', ['$window' , FormSchemaService ])
And in my controllers: function () { 'use strict';
在我的控制器中: function () { 'use strict';
angular.module('myApp').controller('MyController', MyController);
MyEncapsulatorController.$inject = ['$scope', 'FormSchemaService'];
function MyController($scope, formSchemaService) {
// using the already configured formSchemaService
formSchemaService.buildForm();
So far this is pure angular and javascript service oriented programming. But the legacy integration comes here :
到目前为止,这是纯 angular 和 javascript 面向服务的编程。但是遗留集成来了:
<script type="text/javascript">
(function(app){
var conf = app.conf = {
'fields': {
'field1: { // field configuration }
}
} ;
app.dispatchEvent(new Event('register-schema'));
})(window);
</script>
Obviously every approach has it's merits and drawbacks. The advantages and use of this approach depends on your UI. The previously suggested approaches don't work in my case since my form schema and legacy code have no control and knowledge of angular scopes. Hence configuring my app based on angular.element('element-X').scope();
could potentially break the app if we change the scopes around. But if you're app has knowledge of the scoping and can rely on it not changing often, what's suggested previously is a viable approach.
显然,每种方法都有其优点和缺点。这种方法的优点和用途取决于您的 UI。之前建议的方法在我的情况下不起作用,因为我的表单模式和遗留代码无法控制和了解角度范围。因此,angular.element('element-X').scope();
如果我们更改范围,则基于 配置我的应用程序可能会破坏应用程序。但是,如果您的应用程序了解范围界定并且可以依赖它不经常更改,那么之前建议的方法是一种可行的方法。
Hope this helps. Any feedback is also welcome.
希望这可以帮助。也欢迎任何反馈。