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.
Raja
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. поэтому я взял ссылку на объект области и вызвал метод обновления для перекомпиляции, который не работает. Вот пример . Не могли бы вы помочь заставить этот пример работать?
Raja
1
@Raja При вызове угловых метод / выражении вне угловой рамки вы должны вызвать $ объема $ применяются (). Для выполнения правильного объема жизненного цикла обработки исключений, выполняя часы и т.д. jsfiddle.net/fABdD/14
Артем Андреев
1
@ArtemAndreev совершенно прав. @Raja, вы также можете переместить вызов $scope.$apply()в refresh()саму функцию, если это имеет смысл для вашей архитектуры: jsfiddle.net/nfreitas/8xkeh/1
Ной Фрейтас
3
Ссылка angular-1.0.1.min.jsв исходных примерах была 404. Вот обновленные рабочие версии примера @ ArtemAndreev-s: jsfiddle.net/pmorch/fABdD/186 и пример @NoahFreitas
Peter V. Mørch
1
Использование $ compile и манипулирование DOM внутри контроллера приведет вас к созданию непроверенного кода. Директивы должны использоваться для изменения DOM, но не контроллера.
Enzey 03
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);
    });
});

Резюме

Исходный пост, казалось, предполагал, что у вас есть справочник по компиляции $. Очевидно, что это легко, когда у вас есть ссылка, но у меня ее не было, поэтому это был ответ для меня.

Одно предостережение в отношении предыдущего кода

Если вы используете пакет asp.net/mvc со сценарием minify, у вас возникнут проблемы при развертывании в режиме выпуска. Проблема возникает в виде Неперехваченной ошибки: [$ injector: unpr], которая вызвана вмешательством минификатора в код javascript angular.

Вот способ исправить это :

Замените предыдущий фрагмент кода следующей перегрузкой.

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

Это вызвало у меня много горя, прежде чем я собрал все воедино.

jwize
источник
1
Спасибо, так как приведенный выше код работал для меня, и объяснение о доступности компиляции. иногда упускаешь легкие части :)
Abs
После компиляции мне пришлось $ digest.
Knu
2
Абсолютная палочка-выручалочка, я добавил в свой код условную проверку для angular, которая позволяет ему использовать все
преимущества
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' ) ; 
shyammakwana.me
источник
Спасибо, это спасло мне жизнь с пакетом, который использовал UIKit для своих диалогов, с HTML, генерируемым на лету внутри функции $ scope. В качестве побочного примечания мне пришлось изменить селектор $ target в соответствии с моими потребностями, в моем случае $ ["data-ng-controller]"), и закомментировать $ scope.apply (); поскольку эта функция уже была вызвана в
обратном
@zontar вы также можете сделать targetдинамическим, взяв его из аргумента.
shyammakwana.me