AngularJS + JQuery: Jak uzyskać dynamiczną zawartość działającą w angularjs


84

Pracuję nad aplikacją Ajax, używając zarówno jQuery, jak i AngularJS.

Kiedy aktualizuję zawartość (która zawiera powiązania AngularJS) elementu div przy użyciu htmlfunkcji jQuery , powiązania AngularJS nie działają.

Poniżej znajduje się kod tego, co próbuję zrobić:

$(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>

Mam dynamiczną zawartość wewnątrz elementu div z identyfikatorem #dynamicContenti mam przycisk odświeżania, który aktualizowałby zawartość tego elementu div po kliknięciu odświeżania. Przyrost działa zgodnie z oczekiwaniami, jeśli nie odświeżę zawartości, ale po odświeżeniu wiązanie AngularJS przestaje działać.

Może to nie być poprawne w AngularJS, ale początkowo zbudowałem aplikację w jQuery, a później zacząłem używać AngularJS, więc nie mogę przenieść wszystkiego do AngularJS. Każda pomoc w uruchomieniu tego narzędzia w AngularJS jest bardzo mile widziana.


1
Czy jest jakiś szczególny powód, dla którego warto używać JQuery do tej funkcji? Ponieważ jest to ładnie i łatwo omówione przez kątowe: < jsfiddle.net/pkozlowski_opensource/YCrFD/2 >
— pkozlowski.opensource

3
To jest tylko uproszczona wersja mojego prawdziwego przypadku użycia, aby pokazać problem. W rzeczywistej aplikacji dynamiczna zawartość jest generowana przez grails taglib, która jest przekazywana do jquery jako html. Więc nie mogę przenieść całej logiki z tagliba Grails do angularjs, aby uczynić go czystym angularjs.
— Raja

1
@ pkozlowski.opensource, czy link jest martwy? Powinieneś także dołączyć do outdoors.stackexchange.com :)
— Liam,

Odpowiedzi:


115

Musisz wywołać $compilełańcuch HTML przed wstawieniem go do DOM, aby angular miał szansę wykonać powiązanie.

Na twoich skrzypcach wyglądałoby to mniej więcej tak.

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

Oczywiście, $compileaby to zadziałało , należy je wstrzyknąć do kontrolera.

Przeczytaj więcej w $compiledokumentacji .


2
Dzięki Noah, kompilacja działa, gdy robię to w metodzie kontrolera i bezpośrednio wiążę tę metodę kontrolera z kliknięciem przycisku. Oto działający przykład . Ale w mojej rzeczywistej aplikacji musiałem wywołać metodę kontrolera z innej funkcji jquery. więc złapałem odniesienie do obiektu zakresu i wywołałem metodę refresh, aby ponownie skompilować, która nie działa. Oto przykład . Czy możesz pomóc w uruchomieniu tego przykładu.
— Raja

1
@Raja Kiedy wywołujesz metody / wyrażenia kątowe poza strukturą kątową, powinieneś wywołać $ scope. $ Apply
— Artem Andreev

1
@ArtemAndreev dokładnie tak. @Raja można również przesunąć wywołanie $scope.$apply()do refresh()samej funkcji, jeśli ma to sens dla Twojej architektury: jsfiddle.net/nfreitas/8xkeh/1
— Noah Freitas

3
Odnośnik do angular-1.0.1.min.jsoryginalnych przykładów to 404. Oto zaktualizowane robocze wersje przykładu @ ArtemAndreev-s: jsfiddle.net/pmorch/fABdD/186 i @NoahFreitas przykład: jsfiddle.net/pmorch/8xkeh/43
— Peter V. Mørch

1
Używanie $ compile i manipulowanie DOM wewnątrz kontrolera doprowadzi cię do ścieżki posiadania niesprawdzalnego kodu. Dyrektywy powinny być używane do zmiany DOM, nigdy kontrolera.
— Enzey,

57

Inne rozwiązanie na wypadek, gdybyś nie miał kontroli nad zawartością dynamiczną

Działa to, jeśli nie załadowałeś swojego elementu przez dyrektywę (np. Jak w przykładzie w skomentowanym jsfiddles).

Podsumuj swoje treści

Zawiń zawartość w element div, aby można go było wybrać, jeśli używasz JQuery . Możesz także zdecydować się na użycie natywnego javascript, aby uzyskać swój element.

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

Użyj wtryskiwacza kątowego

Możesz użyć poniższego kodu, aby uzyskać odwołanie do $ compile, jeśli go nie masz.

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

Podsumowanie

Oryginalny post wydawał się zakładać, że masz pod ręką odniesienie do $ compile. Jest to oczywiście łatwe, gdy masz odniesienie, ale ja nie, więc to była odpowiedź dla mnie.

Jedno zastrzeżenie poprzedniego kodu

Jeśli używasz pakietu asp.net/mvc ze scenariuszem minify, będziesz mieć kłopoty podczas wdrażania w trybie wydania. Problem pojawia się w postaci błędu Uncaught Error: [$ injector: unpr], który jest spowodowany przez minifier mylący z kątowym kodem javascript.

Oto sposób, aby temu zaradzić :

Zastąp poprzedni fragment kodu następującym przeciążeniem.

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

To wywołało we mnie wiele smutku, zanim złożyłem to w całość.


1
Dzięki, ponieważ powyższy kod zadziałał dla mnie, zakres i wyjaśnienie dotyczące kompilacji jest dostępne. czasami tęsknisz za łatwymi częściami :)
— Abs

Musiałem $ strawić po kompilacji.
— Knu

2
Absolutny ratownik, dodałem warunkowe sprawdzenie kąta w moim kodzie, co pozwala mu korzystać z całej dobroci kątowej po podłączeniu do aplikacji kątowej
— LiamRyan

18

Dodatek do odpowiedzi @ jwize

Ponieważ angular.element(document).injector()dawał błąd injector is not defined Tak więc stworzyłem funkcję, którą możesz uruchomić po wywołaniu AJAX lub po zmianie DOM za pomocą 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();
                    }]);
            }

        }

użyj go, przekazując tylko selektor nowego elementu. lubię to

compileAngularElement( '.user' ) ; 

Dzięki, to uratowało mi życie dzięki pakietowi, który używał UIKit do swoich okien dialogowych, z HTML generowanym w locie wewnątrz funkcji $ scope. Na marginesie, musiałem zmienić selektor $ target, aby odpowiadał moim potrzebom, w moim przypadku $ ["data-ng-controller]") i zakomentować $ scope.apply (); ponieważ ta funkcja została już wywołana w wywołaniu zwrotnym kątowym (więc aplikacja już miała miejsce)
— zontar

@zontar możesz również uczynić targetdynamicznym, biorąc go z argumentu.
— shyammakwana.me
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.