AngularJS + JQuery: come far funzionare il contenuto dinamico in angularjs


84

Sto lavorando su un'app Ajax utilizzando sia jQuery che AngularJS.

Quando aggiorno il contenuto (che contiene i collegamenti AngularJS) di un div utilizzando la htmlfunzione di jQuery , i collegamenti AngularJS non funzionano.

Di seguito è riportato il codice di ciò che sto cercando di fare:

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

Dispongo di contenuto dinamico all'interno di un div con ID #dynamicContente ho un pulsante di aggiornamento che aggiorna i contenuti di questo div quando si fa clic su Aggiorna. L'incremento funziona come previsto se non aggiorno il contenuto, ma dopo l'aggiornamento, l'associazione AngularJS smette di funzionare.

Questo potrebbe non essere valido in AngularJS, ma inizialmente ho creato un'applicazione con jQuery e ho iniziato a utilizzare AngularJS in seguito, quindi non posso migrare tutto su AngularJS. Qualsiasi aiuto per farlo funzionare in AngularJS è molto apprezzato.


1
C'è qualche motivo particolare per utilizzare JQuery per questa funzionalità? Poiché questo è ben e facilmente coperto da angular : < jsfiddle.net/pkozlowski_opensource/YCrFD/2 >
pkozlowski.opensource

3
Questa è solo una versione semplificata del mio caso d'uso reale per mostrare il problema. Nell'applicazione attuale il contenuto dinamico è generato da grails taglib che viene passato a jquery come html. Quindi non posso portare tutta la logica in Grails taglib su angularjs per renderlo puro angularjs.
Raja

1
@ pkozlowski.opensource, quel link è morto? Inoltre dovresti unirti a outdoors.stackexchange.com :)
Liam

Risposte:


115

Devi chiamare $compile la stringa HTML prima di inserirla nel DOM in modo che angular abbia la possibilità di eseguire l'associazione.

Nel tuo violino, sarebbe qualcosa del genere.

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

Ovviamente, $compiledeve essere iniettato nel tuo controller affinché funzioni.

Maggiori informazioni nella $compiledocumentazione .


2
Grazie Noah, la compilazione funziona quando lo faccio in un metodo di controller e associo direttamente tale metodo di controller all'evento di clic del pulsante. Ecco un esempio funzionante . Ma io nella mia app attuale, ho dovuto chiamare il metodo del controller da una diversa funzione jquery. quindi ho preso il riferimento all'oggetto scope e ho chiamato il metodo di aggiornamento per ricompilare che non funziona. Ecco un esempio . Puoi aiutarci a far funzionare questo esempio.
Raja

1
@Raja Quando chiami metodi / espressioni angolari al di fuori del framework angolare dovresti chiamare $ scope. $ Apply () per eseguire il ciclo di vita corretto dell'ambito della gestione delle eccezioni, esecuzione di orologi, ecc. Jsfiddle.net/fABdD/14
Artem Andreev

1
@ArtemAndreev ha esattamente ragione. @Raja potresti anche spostare la chiamata $scope.$apply()nella refresh()funzione stessa se ha senso per la tua architettura: jsfiddle.net/nfreitas/8xkeh/1
Noah Freitas

3
Il collegamento agli angular-1.0.1.min.jsesempi originali era 404. Ecco le versioni funzionanti aggiornate di @ ArtemAndreev-s esempio: jsfiddle.net/pmorch/fABdD/186 e @NoahFreitas esempio: jsfiddle.net/pmorch/8xkeh/43
Peter V. Mørch

1
L'utilizzo di $ compile e la manipolazione di DOM all'interno di un controller ti porteranno lungo un percorso per avere codice non verificabile. Le direttive dovrebbero essere usate per cambiare il DOM mai un controller.
Enzey

57

Un'altra soluzione nel caso in cui non si abbia il controllo sui contenuti dinamici

Funziona se non hai caricato il tuo elemento tramite una direttiva (es. Come nell'esempio nei jsfiddles commentati).

Racchiudi i tuoi contenuti

Avvolgi il tuo contenuto in un div in modo da poterlo selezionare se stai usando JQuery . Puoi anche scegliere di utilizzare javascript nativo per ottenere il tuo elemento.

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

Usa l'iniettore angolare

Puoi usare il codice seguente per ottenere un riferimento a $ compile se non ne hai uno.

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

Sommario

Il post originale sembrava presumere che tu avessi un riferimento a $ compile a portata di mano. Ovviamente è facile quando hai il riferimento, ma non l'ho fatto, quindi questa era la risposta per me.

Un avvertimento del codice precedente

Se stai utilizzando un bundle asp.net/mvc con lo scenario minify, ti troverai nei guai quando esegui la distribuzione in modalità di rilascio. Il problema si presenta sotto forma di Uncaught Error: [$ injector: unpr] che è causato dal minifier che interferisce con il codice javascript angolare.

Ecco il modo per rimediare :

Sostituisci lo snippet di codice precedente con il seguente overload.

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

Questo mi ha causato molto dolore prima di metterlo insieme.


1
Grazie perché il codice sopra ha funzionato per me e la spiegazione sulla compilazione è disponibile. a volte ti mancano le parti facili :)
Abs

Ho dovuto $ digerire dopo la compilazione.
Knu

2
Salvagente assoluto, ho aggiunto un controllo condizionale per angolare nel mio codice che gli consente di utilizzare tutta la bontà angolare quando collegato a un'app angolare
LiamRyan

18

Aggiunta alla risposta di @ jwize

Perché angular.element(document).injector()stava dando un errore injector is not defined Quindi, ho creato una funzione che puoi eseguire dopo la chiamata AJAX o quando DOM viene modificato usando 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();
                    }]);
            }

        }

usalo passando solo il selettore del nuovo elemento. come questo

compileAngularElement( '.user' ) ; 

Grazie, questo mi ha salvato la vita con un pacchetto che utilizzava UIKit per le sue finestre di dialogo, con HTML generato al volo all'interno di una funzione $ scope. Come nota a margine, ho dovuto cambiare il selettore di $ target in base alle mie esigenze, nel mio caso $ ["data-ng-controller]"), e commentare $ scope.apply (); poiché questa funzione è stata già chiamata in un callback angolare (quindi l'applicazione era già in corso)
zontar

@zontar puoi anche renderlo targetdinamico prendendolo dall'argomento.
shyammakwana.me
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.