Викличте AngularJS зі застарілого коду


180

Я використовую 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>).

Дякую!


1
Зауважте, що у jsfiddle над інжектором виходить без націлювання на елемент у програмі, що використовує var injector = angular.injector(['ng', 'MyApp']);. Це дасть вам абсолютно новий контекст і дублікат myService. Це означає, що ви отримаєте два екземпляри служби та моделі та додасте дані в неправильне місце. Натомість слід орієнтуватися на елемент у програмі за допомогою angular.element('#ng-app').injector(['ng', 'MyApp']). Після цього ви можете використовувати $ apply для обертання змін моделі.
Thanh Nguyen

Відповіді:


293

Інтероп ззовні від кутового до кутового - це те саме, що налагоджувати кутовий додаток або інтегруватись із сторонніми бібліотеками.

Для будь-якого елемента 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.
});

1
це працює, але я б хотів, щоб був якийсь спосіб дістатися з модуля безпосередньо до його сфери - це можливо? Повернення вибору [ng-app]root-вузла здається назад, коли я вже маю посилання на Модуль ...
mindplay.dk

5
Я не можу змусити це працювати: я дзвоню angular.element(document.getElementById(divName)).scope(), але я не в змозі викликати з нього жодної функції, він просто повертається "невизначено" в консолі.
Еміль

1
Навіть я зіткнувся з тією ж проблемою, яку описав вище @Emil, вона повертається невизначеною. Будь-яка допомога?
Бібін

5
element (). domain () не буде працювати, якщо дані налагодження вимкнено, що є рекомендацією для виробництва. Це не робить це марним у цьому сценарії? Це буде лише для тестування / налагодження.
К. Норберт

4
Ви не хочете робити це в куті 1.3. Команда Angular не мала наміру називати ".scope ()" елементами у виробничому коді. Це мало бути інструментом налагодження. Отже, починаючи з кутового 1.3, ви можете вимкнути це. Кутовий перестане прив’язувати область до елемента за допомогою функції .data jQuery. Це пришвидшить ваш додаток. Крім того, передача областей кеш-функцій jquery створить витоки пам'яті. Отже, вам слід вимкнути це, щоб пришвидшити додаток. На сайті Angular є посібник із виробництва, який слід використовувати, щоб дізнатися більше.
морозний

86

Місько дав правильну відповідь (очевидно), але дехто з нас, новачків, може, потребуватиме його додатково спростити.

Якщо ви хочете зателефонувати за кодом AngularJS із застарілих програм, подумайте про код AngularJS як "мікро-додаток", що існує в захищеному контейнері у вашій застарілій програмі. Ви не можете телефонувати на нього безпосередньо (з дуже поважної причини), але ви можете здійснювати віддалені дзвінки за допомогою об’єкта $ range.

Щоб використовувати об'єкт $ range, вам потрібно отримати ручку $ range. На щастя, це зробити дуже просто.

Ви можете використовувати ідентифікатор будь-якого елемента HTML у вашому HTML "мікро-додатку" AngularJS, щоб отримати ручку діапазону програми "AngularJS" $.

Наприклад, скажімо, що ми хочемо викликати пару функцій в нашому контролері AngularJS, таких як sayHi () і sayBye (). У HTML AngularJS (view) у нас є div з ідентифікатором "MySuperAwesomeApp". Ви можете використовувати наступний код у поєднанні з jQuery, щоб отримати ручку $ range:

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


4
Зауважте, що відповіді, що стосуються лише посилань, не відштовхують, відповіді SO повинні бути кінцевою точкою пошуку рішення (проти ще однієї зупинки посилань, яка, як правило, затягується з часом). Просимо додати тут окремий конспект, зберігаючи посилання як орієнтир.
Клеопатра

Приємне додаткове уточнення. Дякую.
Джо

1
Красиве пояснення! дозволив мені обійти перевірку форми, роблячи це:<input type="button" onclick="angular.element(this).scope().edit.delete();" value="delete">
Purefan

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

14
Вищезазначене працює, коли програма та контролер співіснують в одному елементі. Для більш складних додатків, які використовують директиву ng-перегляду для шаблону, ви повинні отримати перший елемент у представленні, а не елемент DOM у всій програмі. Мені довелося обминути елементи за допомогою document.getElementsByClassName ('ng-range'); список вузлів, щоб з'ясувати правильний елемент DOM для області захоплення.
goosemanjack

Я знаю, що це справді стара тема, але я думаю, що я стикаюся з цим питанням. У когось є якийсь код, який показує, як пройти по списку, щоб з’ясувати елемент DOM для захоплення?
ДжерріКур

Ігноруй моє запитання. Мені вдалося отримати цю роботу просто за допомогою document.getElementById ("будь-яка-контроль-що-має-An-NG-директива"). Oblast ().
ДжерріКур

якщо ви використовуєте ng-view і розділите свої погляди на власні файли. ви можете поставити і idв верхньому HTML-елементі, потім зробіть document.getElementById()цей ідентифікатор. Це дає вам доступ до сфери дії цього контролера. методи / властивості тощо ... просто поставити точну крапку на коментар goosemanjack.
ftravers

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 

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>

Я заявляю про свою модель

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

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

Узагальнено як директива для багаторазового використання:

Я створив директиву "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 ('rangeDestroyed'), коли фактичний елемент видаляється з DOM. Якщо це не працює, може допомогти запускання події на самому документі замість елемента. (див. app.js) скрипт у демонстраційному планері.


3

Я знаю, що це давнє запитання, але я розглядав варіанти зробити це нещодавно, тому думав, що я викладу свої висновки тут, на випадок, якщо це комусь корисно.

У більшості випадків, якщо є необхідність взаємодії зовнішнього застарілого коду із станом інтерфейсу користувача або внутрішнім функціонуванням програми, сервіс може бути корисним для усунення цих змін. Якщо зовнішній код безпосередньо взаємодіє з вашим кутовим контролером, компонентом або директивою, ви сильно зв’язуєте додаток зі своїм застарілим кодом, що є поганою новиною.

Що я в кінцевому підсумку використовував у моєму випадку - це комбінація глобальних глобальних мереж (тобто вікон) та обробки подій. Мій код має інтелектуальний механізм генерації форм, який вимагає виходу 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 ])

А в моїх контролерах: function () {'використовувати строгий';

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(); може потенційно зламати додаток, якщо ми змінимо область застосування. Але якщо у вас є програма, яка знає сферу застосування, і ви можете розраховувати на те, що вона не змінюється часто, те, що пропонується раніше, є життєздатним підходом.

Сподіваюся, це допомагає. Будь-який відгук також вітається.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.