AngularJS + JQuery: So erstellen Sie dynamische Inhalte in AngularJs

84

Ich arbeite an einer Ajax-App mit jQuery und AngularJS.

Wenn ich den Inhalt (der AngularJS-Bindungen enthält) eines div mit der htmlFunktion von jQuery aktualisiere, funktionieren die AngularJS-Bindungen nicht.

Das Folgende ist der Code dessen, was ich versuche zu tun:

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

Ich habe dynamischen Inhalt in einem Div mit der ID #dynamicContentund ich habe eine Aktualisierungsschaltfläche, die den Inhalt dieses Div aktualisiert, wenn auf Aktualisieren geklickt wird. Inkrement funktioniert wie erwartet, wenn ich den Inhalt nicht aktualisiere, aber nach dem Aktualisieren funktioniert die AngularJS-Bindung nicht mehr.

Dies ist in AngularJS möglicherweise nicht gültig, aber ich habe zunächst eine Anwendung mit jQuery erstellt und später mit der Verwendung von AngularJS begonnen, sodass ich nicht alles nach AngularJS migrieren kann. Jede Hilfe, um dies in AngularJS zum Laufen zu bringen, wird sehr geschätzt.

Raja
quelle
1
Gibt es einen bestimmten Grund für die Verwendung von JQuery für diese Funktionalität? Da dies schön und leicht durch Winkel abgedeckt wird: < jsfiddle.net/pkozlowski_opensource/YCrFD/2 >
pkozlowski.opensource
3
Dies ist nur eine vereinfachte Version meines realen Anwendungsfalls, um das Problem aufzuzeigen. In der tatsächlichen Anwendung wird dynamischer Inhalt von grails taglib generiert, der als HTML an jquery übergeben wird. Daher kann ich nicht alle Logik in Grails Taglib auf AngularJS portieren, um es zu reinen AngularJS zu machen.
Raja
1
@ pkozlowski.opensource, dieser Link ist tot? Auch sollten Sie sich im Freien
Liam

Antworten:

115

Sie müssen $compiledie HTML-Zeichenfolge aufrufen, bevor Sie sie in das DOM einfügen, damit Angular die Möglichkeit hat, die Bindung auszuführen.

In deiner Geige würde es ungefähr so ​​aussehen.

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

$compileMuss natürlich in Ihren Controller injiziert werden, damit dies funktioniert.

Lesen Sie mehr in der $compileDokumentation .

Noah Freitas
quelle
2
Dank Noah funktioniert das Kompilieren, wenn ich es in einer Controller-Methode mache und diese Controller-Methode direkt binde, um auf das Ereignis der Schaltfläche zu klicken. Hier arbeitet Beispiel . Aber ich musste in meiner eigentlichen App die Controller-Methode von einer anderen jquery-Funktion aus aufrufen. Also habe ich den Verweis auf das Bereichsobjekt abgerufen und die Aktualisierungsmethode aufgerufen, um sie neu zu kompilieren, was nicht funktioniert. Hier ist ein Beispiel . Können Sie helfen, dieses Beispiel zum Laufen zu bringen?
Raja
1
@Raja Wenn Sie Winkelmethoden / -ausdrücke außerhalb des Winkelrahmens aufrufen, sollten Sie $ scope. $ Apply () aufrufen , um einen ordnungsgemäßen Bereichslebenszyklus für die Ausnahmebehandlung, die Ausführung von Uhren usw. durchzuführen
Artem Andreev
1
@ArtemAndreev genau richtig. @ Raja Sie können den Aufruf auch $scope.$apply()in die refresh()Funktion selbst verschieben, wenn dies für Ihre Architektur sinnvoll ist
Noah Freitas
3
Der Link zu angular-1.0.1.min.jsin den Originalbeispielen war 404. Hier sind aktualisierte Arbeitsversionen von @ ArtemAndreev-s Beispiel: jsfiddle.net/pmorch/fABdD/186 und @NoahFreitas Beispiel: jsfiddle.net/pmorch/8xkeh/43
Peter V. Mørch
1
Wenn Sie $ compile verwenden und DOM in einem Controller bearbeiten, gelangen Sie auf den Weg zu nicht testbarem Code. Direktiven sollten zum Ändern des DOM niemals als Controller verwendet werden.
Enzey
57

Eine weitere Lösung für den Fall, dass Sie keine Kontrolle über dynamische Inhalte haben

Dies funktioniert, wenn Sie Ihr Element nicht über eine Direktive geladen haben (dh wie im Beispiel in den kommentierten jsfiddles).

Packen Sie Ihre Inhalte ein

Wickeln Sie Ihren Inhalt in ein div, damit Sie ihn auswählen können, wenn Sie JQuery verwenden . Sie können auch natives Javascript verwenden, um Ihr Element zu erhalten.

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

Winkelinjektor verwenden

Sie können den folgenden Code verwenden, um einen Verweis auf $ compile abzurufen, wenn Sie keinen haben.

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

Zusammenfassung

Der ursprüngliche Beitrag schien davon auszugehen, dass Sie eine $ compile-Referenz zur Hand hatten. Es ist offensichtlich einfach, wenn Sie die Referenz haben, aber ich habe es nicht getan, also war dies die Antwort für mich.

Eine Einschränkung des vorherigen Codes

Wenn Sie ein asp.net / mvc-Bundle mit Minify-Szenario verwenden, treten Probleme bei der Bereitstellung im Release-Modus auf. Das Problem tritt in Form eines nicht erfassten Fehlers auf: [$ injor: unpr], der durch den Minifier verursacht wird, der mit dem eckigen Javascript-Code herumspielt.

Hier ist der Weg, um Abhilfe zu schaffen :

Ersetzen Sie das vorherige Code-Snippet durch die folgende Überladung.

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

Dies verursachte mir viel Kummer, bevor ich es zusammensetzte.

jwize
quelle
1
Vielen Dank, da der obige Code für mich funktioniert hat und die Erklärung zum Kompilieren verfügbar ist. manchmal vermisst du die einfachen Teile :)
Abs
Ich musste nach dem Kompilieren $ verdauen.
Knu
2
Absoluter Lebensretter, ich habe meinem Code eine bedingte Winkelprüfung hinzugefügt, die es ihm ermöglicht, alle
Winkelgüte
18

Ergänzung zu @ jwizes Antwort

Weil angular.element(document).injector()es einen Fehler gab injector is not defined Also habe ich eine Funktion erstellt, die Sie nach einem AJAX-Aufruf oder wenn DOM mit jQuery geändert wird, ausführen können.

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

        }

Verwenden Sie es, indem Sie nur den Selektor eines neuen Elements übergeben. so was

compileAngularElement( '.user' ) ; 
shyammakwana.me
quelle
Vielen Dank, dies hat mir das Leben gerettet mit einem Paket, das UIKit für seine Dialoge verwendete, wobei HTML im laufenden Betrieb innerhalb einer $ scope-Funktion generiert wurde. Als Randnotiz musste ich den Selektor des $ target an meine Bedürfnisse anpassen, in meinem Fall $ ["data-ng-controller]") und $ scope.apply () auskommentieren; da diese Funktion bereits in einem
Winkelrückruf
@zontar Sie können auch targetdynamisch machen, indem Sie es aus dem Argument nehmen.
Shyammakwana.me