AngularJS + JQuery: Як змусити динамічний вміст працювати в angularjs


84

Я працюю над додатком Ajax, використовуючи як jQuery, так і AngularJS.

Коли я оновлюю вміст (який містить прив'язки AngularJS) div за допомогою htmlфункції jQuery , прив'язки AngularJS не працюють.

Далі наводиться код того, що я намагаюся зробити:

$(document).ready(function() {
  $("#refreshButton").click(function() {
    $("#dynamicContent").html("<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>")
  });
});
</style><script src="http://docs.angularjs.org/angular-1.0.1.min.js"></script><style>.ng-invalid {
  border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="">
  <div id='dynamicContent'>
    <button ng-click="count = count + 1" ng-init="count=0">
        Increment
      </button>
    <span>count: {{count}} </span>
  </div>


  <button id='refreshButton'>
    Refresh
  </button>
</div>

У мене є динамічний вміст усередині div із ідентифікатором #dynamicContent, і у мене є кнопка оновлення, яка оновлює вміст цього div, коли натискається оновлення. Інкремент працює належним чином, якщо я не оновлюю вміст, але після оновлення прив'язка AngularJS перестає працювати.

Це може бути недійсним у AngularJS, але спочатку я створив додаток з jQuery і пізніше почав використовувати AngularJS, тому я не можу перенести все на AngularJS. Будь-яка допомога з отриманням цієї роботи в AngularJS дуже вдячна.


1
Чи існує якась особлива причина використання JQuery для цієї функції? Оскільки це приємно та легко покривається angular : < jsfiddle.net/pkozlowski_opensource/YCrFD/2 >
pkozlowski.opensource

3
Це лише спрощена версія мого реального випадку використання, щоб показати проблему. У реальному додатку динамічний вміст генерується за допомогою grails taglib, який передається jquery як html. Тож я не можу перенести всю логіку у grails taglib на angularjs, щоб зробити її чистою angularjs.
Раджа

1
@ pkozlowski.opensource, це посилання мертве? Також вам слід приєднатися outdoors.stackexchange.com :)
Ліам

Відповіді:


115

Вам потрібно зателефонувати $compileдо рядка HTML перед тим, як вставляти його в DOM, щоб angular отримав можливість виконати прив'язку.

У вашій скрипці це виглядало б приблизно так.

$("#dynamicContent").html(
  $compile(
    "<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>"
  )(scope)
);

Очевидно, що для роботи це $compileпотрібно ввести у ваш контролер.

Детальніше читайте в $compileдокументації .


2
Дякую Ною, компіляція працює, коли я роблю це методом контролера і безпосередньо прив'язую цей метод контролера до натискання події кнопки. Ось робочий приклад . Але мені у моєму фактичному додатку довелося викликати метод контролера з іншої функції jquery. тому я схопив посилання на об'єкт обсягу і викликав метод оновлення для перекомпіляції, який не працює. Ось приклад . Чи можете ви допомогти з тим, щоб цей приклад працював?
Раджа

1
@Raja Коли ви викликаєте angular методи / вираз за межі кутового фреймворку, вам слід викликати $ scope. $ Apply () для виконання належного життєвого циклу обробки винятків, виконання годинників тощо. Jsfiddle.net/fABdD/14
Артем Андрєєв

1
@ArtemAndreev абсолютно правильно. @Raja, ви також можете перенести виклик до $scope.$apply()самої refresh()функції, якщо це має сенс для вашої архітектури: jsfiddle.net/nfreitas/8xkeh/1
Noah Freitas

3
Посилання на angular-1.0.1.min.jsоригінальні приклади було 404. Ось оновлені робочі версії прикладу @ ArtemAndreev-s: jsfiddle.net/pmorch/fABdD/186 та @NoahFreitas приклад: jsfiddle.net/pmorch/8xkeh/43
Петро В. Морх

1
Використання $ compile та маніпулювання DOM всередині контролера призведе вас до шляху неперевіреного коду. Слід використовувати директиви для зміни DOM, ніколи контролера.
Enzey

57

Ще одне рішення у випадку, якщо у вас немає контролю над динамічним вмістом

Це працює, якщо ви не завантажили свій елемент за допомогою директиви (тобто, як у прикладі в коментованому jsfiddles).

Завершіть свій вміст

Оберніть свій вміст у div, щоб ви могли вибрати його, якщо використовуєте JQuery . Ви також вирішили використовувати власний javascript для отримання свого елемента.

<div class="selector">
    <grid-filter columnname="LastNameFirstName" gridname="HomeGrid"></grid-filter>
</div>

Використовуйте кутовий інжектор

Ви можете використовувати наступний код, щоб отримати посилання на $ compile, якщо у вас його немає.

$(".selector").each(function () {
    var content = $(this);
    angular.element(document).injector().invoke(function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    });
});

Резюме

Оригінальний пост, здавалося, припускав, що у вас є зручна довідка $ compile. Очевидно, це просто, коли у вас є посилання, але я не зробив цього, це була для мене відповідь.

Одне попередження попереднього коду

Якщо ви використовуєте пакет asp.net/mvc зі сценарієм мініфікації, у вас виникнуть проблеми під час розгортання в режимі випуску. Проблема виникає у вигляді непоміченої помилки: [$ injector: unpr], яка викликана перекручуванням мініфікатора з кутовим кодом JavaScript.

Ось спосіб виправити це :

Замініть фрагмент коду превіуса таким перевантаженням.

...
angular.element(document).injector().invoke(
[
    "$compile", function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    }
]);
...

Це викликало у мене багато горя, перш ніж я склав його разом.


1
Дякую, що наведений вище код працював для мене, і пояснення щодо компіляції доступне. іноді ви пропускаєте легкі частини :)
Абс

Після компіляції мені довелося переварити $.
Кну

2
Абсолютний рятувальник, я додав умовну перевірку на кутовість у своєму коді, що дозволяє йому використовувати всю кутову
якість

18

Додаток до відповіді @ jwize

Оскільки angular.element(document).injector()давала помилку injector is not defined Отже, я створив функцію, яку ви можете запускати після виклику AJAX або коли DOM змінюється за допомогою jQuery.

  function compileAngularElement( elSelector) {

        var elSelector = (typeof elSelector == 'string') ? elSelector : null ;  
            // The new element to be added
        if (elSelector != null ) {
            var $div = $( elSelector );

                // The parent of the new element
                var $target = $("[ng-app]");

              angular.element($target).injector().invoke(['$compile', function ($compile) {
                        var $scope = angular.element($target).scope();
                        $compile($div)($scope);
                        // Finally, refresh the watch expressions in the new element
                        $scope.$apply();
                    }]);
            }

        }

використовувати його, передавши лише селектор нового елемента. подобається це

compileAngularElement( '.user' ) ; 

Дякуємо, це врятувало мені життя за допомогою пакета, який використовував UIKit для своїх діалогових вікон, а HTML створювався на льоту всередині функції $ scope. Як допоміжне зауваження, мені довелося змінити селектор цілі $ відповідно до моїх потреб, у моєму випадку $ ["data-ng-controller]"), і прокоментувати $ scope.apply (); оскільки ця функція була викликана вже при кутовому зворотному виклику (значить, заявка вже мала місце)
zontar

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