Я использую 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? Может ли контроллер прослушивать события из службы? В одном эксперимент (нажмите для скрипки) Мне казалось, что я могу получить доступ к службе, но обновление данных службы не отражается в view (в примере a <option>
следует добавить в <select>
).
Спасибо!
Ответ 1
Взаимодействие извне angular в angular такое же, как отладка приложения angular или интеграция с сторонней библиотекой.
Для любого элемента DOM вы можете сделать это:
-
angular.element(domElement).scope()
, чтобы получить текущую область для элемента
-
angular.element(domElement).injector()
, чтобы получить текущий инжектор приложения
-
angular.element(domElement).controller()
, чтобы удержать экземпляр ng-controller
.
Из инжектора вы можете получить любую услугу в приложении angular. Аналогично из области действия вы можете ссылаться на любые методы, которые были опубликованы в ней.
Имейте в виду, что любые изменения в модели angular или любые вызовы метода в области необходимо обернуть в $apply()
следующим образом:
$scope.$apply(function(){
// perform any model changes or method invocations here on angular app.
});
Ответ 2
Миско дал правильный ответ (очевидно), но некоторые из нас, новичков, могут нуждаться в дальнейшем упрощении.
Если при вызове кода AngularJS из унаследованных приложений подумайте о коде AngularJS как о "микроприложении", который существует в защищенном контейнере в устаревшем приложении. Вы не можете звонить на него напрямую (по очень веской причине), но вы можете делать удаленные вызовы с помощью объекта $scope.
Чтобы использовать объект $scope, вам нужно получить дескриптор $scope. К счастью, это очень легко сделать.
Вы можете использовать идентификатор любого HTML-элемента в вашем HTML файле "micro-app" в формате AngularJS, чтобы получить дескриптор области приложения AngularJS.
В качестве примера предположим, что мы хотим назвать несколько функций в нашем контроллере AngularJS, таких как sayHi() и sayBye(). В HTML (view) AngularJS у нас есть 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
Ответ 3
Наибольшее объяснение концепции, которую я нашел, находится здесь:
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);
});
Ответ 4
Благодаря предыдущему сообщению я могу обновить мою модель с асинхронным событием.
<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;
});
};
Ответ 5
В дополнение к другим ответам.
Если вы не хотите получать доступ к методу в контроллере, но хотите получить доступ к службе напрямую, вы можете сделать что-то вроде этого:
// 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
Ответ 6
Более безопасный и эффективный способ, особенно когда отладочные данные отключены, заключается в использовании общей переменной для удержания функции обратного вызова. Контроллер 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;
}
}]);
Обобщенный как директива многократного использования:
Я создал директиву "exposeScope", которая работает аналогичным образом, но использование проще:
<div ng-controller="myController" expose-scope="aVariableNameForThisScope">
<span expose-scope='anotherVariableNameForTheSameScope" />
</div>
Здесь хранится текущая область действия (которая задана функции ссылки для директивы) в глобальном объекте "облачности", который является держателем для всех областей. Значение, предоставляемое атрибуту директивы, используется как имя свойства области в этом глобальном объекте.
Смотрите демо здесь. Как я показал в демонстрации, вы можете запускать события jQuery, когда область хранится и удаляется из глобального объекта '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'), когда фактический элемент удален из DOM. Если это не сработает, может помочь запуск события на самом документе, а не на элемент. (см. app.js) script в демонстрационном плунжере.
Ответ 7
Я знаю, что это старый вопрос, но я смотрел варианты, чтобы сделать это в последнее время, поэтому я подумал, что я делаю свои выводы здесь, если это полезно для всех.
В большинстве случаев, если необходимо, чтобы внешний код устаревшего взаимодействия взаимодействовал с состоянием пользовательского интерфейса или внутренней работой приложения, услуга могла бы быть полезной для абстрагирования этих изменений. Если внешний код взаимодействует напрямую с вашим контроллером, компонентом или директивой angular, вы сильно связываете свое приложение с устаревшим кодом, который является плохим новостям.
То, что я использовал в моем случае, представляет собой комбинацию доступных глобальных браузеров (например, окна) и обработки событий. В моем коде есть интеллектуальный механизм создания форм, который требует вывода 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 инжектора, как ожидалось:
angular.module('myApp.services').
service('FormSchemaService', ['$window' , FormSchemaService ])
И в моих контроллерах:
function() { 'use strict';
angular.module('myApp').controller('MyController', MyController);
MyEncapsulatorController.$inject = ['$scope', 'FormSchemaService'];
function MyController($scope, formSchemaService) {
// using the already configured formSchemaService
formSchemaService.buildForm();
Пока это чистое angular и javascript сервис-ориентированное программирование. Но унаследованная интеграция здесь:
<script type="text/javascript">
(function(app){
var conf = app.conf = {
'fields': {
'field1: { // field configuration }
}
} ;
app.dispatchEvent(new Event('register-schema'));
})(window);
</script>
Очевидно, что каждый подход имеет свои достоинства и недостатки. Преимущества и использование этого подхода зависят от вашего пользовательского интерфейса. Ранее предложенные подходы не работают в моем случае, так как моя схема формы и устаревший код не имеют контроля и знаний в областях angular. Следовательно, настройка моего приложения на основе angular.element('element-X').scope();
может потенциально нарушить приложение, если мы изменим области вокруг. Но если у вас есть приложение, которое обладает знаниями о области охвата и может полагаться на то, что оно не меняется часто, то предлагаемое ранее является жизнеспособным подходом.
Надеюсь, это поможет. Любые отзывы также приветствуются.