Позвоните в AngularJS от устаревшего кода
Я использую AngularJS для создания HTML-элементов управления, которые взаимодействуют с устаревшим приложением Flex. Все обратные вызовы из приложения 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>
).
спасибо!
7 ответов:
взаимодействие снаружи углового к угловому такое же как отлаживать угловое применение или интегрировать с библиотекой третьей стороны.
для любого элемента 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, вам нужно получить дескриптор $масштаб. К счастью, это очень легко сделать.
вы можете использовать идентификатор любого HTML-элемента в вашем AngularJS "микро-приложение" HTML, чтобы получить дескриптор AngularJS app $scope.
в качестве примера предположим, что мы хотим вызвать пару функций в нашем контроллере AngularJS, таких как sayHi() и sayBye(). В AngularJS HTML (view) у нас есть div с идентификатором "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/
Я показал это в слайд-шоу для группы Ottawa 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); });
благодаря предыдущему сообщению, я могу обновить свою модель с асинхронным событием.
<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
более безопасный и эффективный способ, особенно когда отладочные данные выключены, - использовать общую переменную для хранения функции обратного вызова. Ваш угловой контроллер реализует эту функцию для возврата своих внутренних элементов во внешний код.
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>
это сохраняет текущую область (которая дана функция link директивы) в глобальном объекте 'scopes', который является держателем для всех областей. Значение, предоставленное атрибуту директивы, используется в качестве имени свойства области в этом глобальном объекте.
посмотреть демо здесь. Как я показал в демо, вы можете запускать события jQuery, когда область сохраняется и удаляется из объекта global 'scopes'.
<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>
обратите внимание, что я не проверял on ('scopeDestroyed'), когда фактический элемент удаляется из дома. Если это не работает, запуск события на самом документе вместо элемента может помочь. ( см. Приложение.в JS ) скрипт в демо plunker.
Я знаю, что это старый вопрос, но я недавно рассматривал варианты, чтобы сделать это, поэтому я думал, что я поместил свои выводы здесь, если это кому-то полезно.
в большинстве случаев, если существует необходимость во внешнем устаревшем коде для взаимодействия с состоянием пользовательского интерфейса или внутренней работой приложения, служба может быть полезна для абстрагирования этих изменений. Если внешний код взаимодействует непосредственно с вашим угловым контроллером, компонентом или директивой, вы связывание вашего приложения в значительной степени с вашим устаревшим кодом, что является плохой новостью.
то, что я в конечном итоге использовал в моем случае, является комбинацией доступных для браузера глобалов (т. е. окна ) и обработки событий. Мой код имеет интеллектуальный механизм генерации форм, который требует вывода JSON из CMS для инициализации форм. Вот что я сделал :
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 ])
и в моих контроллерах: функция () { 'use strict';
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>
очевидно, что каждый подход имеет достоинства и недостатки. Преимущества и использование этого подхода зависит от вашего пользовательского интерфейса. Ранее предложенные подходы не работают в моем случае, так как моя схема формы и устаревший код не имеют контроля и знания угловых областей. Следовательно, настройка моего приложения на основе
angular.element('element-X').scope();
мог потенциально сломать приложение, если мы изменим области вокруг. Но если вы приложение имеет знания об области и может полагаться на него не меняется часто, то, что было предложено ранее является жизнеспособным подходом.надеюсь, что это помогает. Любая обратная связь приветствуется.