Sostituisci il nodo ng-include con il modello?


85

Un po 'nuovo per angolare. È possibile sostituire il nodo ng-include con il contenuto del modello incluso? Ad esempio, con:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <div ng-include src="'test.html'"></div>
</div>

L'html generato è:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <div ng-include src="'test.html'">
        <span class="ng-scope"> </span>
        <p>Test</p>
        <span class="ng-scope"> </span>
    </div>
</div>

Ma quello che voglio è:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <p>Test</p>
</div>

1
rimuovere le virgolette singole da 'test.html'utilizzare il modello anziché l'URL
— charlietfl

1
leggendo i commenti del documento per ng-include, sembra che racchiuda la stringa tra virgolette singole per template e senza per url. Inoltre, domanda relativa allo
— stackoverflow

stai leggendo documenti e post a cui ti sei collegato al
— contrario

Risposte:


134

Ho avuto lo stesso problema e volevo ancora che le funzionalità di ng-include includessero un modello dinamico. Stavo costruendo una barra degli strumenti Bootstrap dinamica e avevo bisogno di un markup più pulito per applicare correttamente gli stili CSS.

Ecco la soluzione che ho pensato per coloro che sono interessati:

HTML:

<div ng-include src="dynamicTemplatePath" include-replace></div>

Direttiva doganale:

app.directive('includeReplace', function () {
    return {
        require: 'ngInclude',
        restrict: 'A', /* optional */
        link: function (scope, el, attrs) {
            el.replaceWith(el.children());
        }
    };
});

Se questa soluzione fosse utilizzata nell'esempio precedente, l'impostazione di scope.dynamicTemplatePath su "test.html" comporterebbe il markup desiderato.


Richiede
— angular


Funziona con angolare 1.2.5+. Per 1.2.4, se hai un ng-include che ng-include un altro, fallisce. Immagino a causa del # 5247 , ma non ne sono sicuro. Guarda tu stesso Changelog . Ecco un Plunkr che mostra questo problema con 1.2.4 (passare all'angolo 1.2.5 e vedere che funziona! :-)
— Peter V. Mørch

9
Tieni presente che tale manipolazione DOM è piuttosto complicata. C'è un problema se l'elemento radice del modello incluso utilizza qualcosa come ng-repeat. Non sarà in grado di inserire risultati in DOM.
— Guria

1
Si prega di vedere la mia risposta a questo, questo fallirà perché la funzione prelink sarà già eseguita negli elementi figlio.
— Sai Dubbaka

28

Quindi, grazie a @ user1737909, ho capito che ng-include non è la strada da percorrere. Le direttive rappresentano l'approccio migliore e più esplicito.

var App = angular.module('app', []);

App.directive('blah', function() {
    return {
        replace: true,
        restrict: 'E',  
        templateUrl: "test.html"
    };
});

In html:

<blah></blah>

2
Grazie! stavo cercando una soluzione ng-include ma questo mi ha aiutato a capire che le direttive sono migliori
— Matt Kim

Tieni presente che replace:truenei modelli è contrassegnato per la deprecazione . Eviterei di utilizzare questa soluzione a causa dello stato di deprecazione.
— Peter V. Mørch

@ PeterV.Mørch Grazie. Per chi fosse interessato, questo è il commit: github.com/angular/angular.js/commit/… . Sembra che sia stato deprecato a causa della sua complessità (e forse per altri motivi).
— SunnySydeUp

15

Ho avuto lo stesso problema, al mio foglio di stile CSS di terze parti non piaceva l'elemento DOM aggiuntivo.

La mia soluzione era semplicissima. Sposta semplicemente ng-include 1 in alto. Quindi invece di

<md-sidenav flex class="md-whiteframe-z3" md-component-id="left" md-is-locked-open="$media('gt-md')">
  <div ng-include="myService.template"></span>
</md-sidenav>

Ho semplicemente fatto:

<md-sidenav flex class="md-whiteframe-z3" md-component-id="left" md-is-locked-open="$media('gt-md')" ng-include="myService.template">
</md-sidenav>

Scommetto che funzionerà nella maggior parte delle situazioni, anche se tecnicamente non è ciò che la domanda sta chiedendo.


10

Un'altra alternativa è scrivere la tua semplice direttiva di sostituzione / inclusione, ad es

    .directive('myReplace', function () {
               return {
                   replace: true,
                   restrict: 'A',
                   templateUrl: function (iElement, iAttrs) {
                       if (!iAttrs.myReplace) throw new Error("my-replace: template url must be provided");
                       return iAttrs.myReplace;
                   }
               };
           });

Questo verrebbe quindi utilizzato come segue:

<div my-replace="test.html"></div>

9

Questo è il modo corretto di sostituire i bambini

angular.module('common').directive('includeReplace', function () {
    return {
        require: 'ngInclude',
        restrict: 'A',
        compile: function (tElement, tAttrs) {
            tElement.replaceWith(tElement.children());
            return {
                post : angular.noop
            };
        }
    };
});

4
Il mio html parziale incluso ha ottenuto alcune ripetizioni ng, e questa è l'unica risposta che li risolve! Molte grazie.
— Saad Benbouzid

Ho dovuto spostare il contenuto della funzione da compilea link, perché il mio elemento era vuoto durante la fase di compilazione.
— itachi

3

La seguente direttiva estende la funzionalità della direttiva nativa ng-include.

Aggiunge un listener di eventi per sostituire l'elemento originale quando il contenuto è pronto e caricato.

Usalo nel modo originale, aggiungi semplicemente l'attributo "sostituisci":

<ng-include src="'src.html'" replace></ng-include>

o con la notazione degli attributi:

<div ng-include="'src.html'" replace></div>

Ecco la direttiva (ricordati di includere il modulo 'include-replace' come dipendenza):

angular.module('include-replace', []).directive('ngInclude', function () {
    return {
        priority: 1000,
        link: function($scope, $element, $attrs){

            if($attrs.replace !== undefined){
                var src = $scope.$eval($attrs.ngInclude || $attrs.src);

                var unbind = $scope.$on('$includeContentLoaded', function($event, loaded_src){
                    if(src === loaded_src){
                        $element.next().replaceWith($element.next().children());
                        unbind();
                    };
                });
            }
        }
    };
});

2

Andrei con una soluzione più sicura di quella fornita da @Brady Isom.

Preferisco fare affidamento onloadsull'opzione fornita da ng-includeper assicurarmi che il modello sia caricato prima di provare a rimuoverlo.

.directive('foo', [function () {
    return {
        restrict: 'E', //Or whatever you need
        scope: true,
        template: '<ng-include src="someTemplate.html" onload="replace()"></ng-include>',
        link: function (scope, elem) {
            scope.replace = function () {
                elem.replaceWith(elem.children());
            };
        }
    };
}])

Non c'è bisogno di una seconda direttiva poiché tutto viene gestito all'interno della prima.


attenzione che con angolare 1.5 il primo figlio nell'elemento direttiva è un commento. Quindi mi sono assicurato di ottenere l'elemento ng-include e poi di sostituirlo con i suoi figli: let ngInclude = angular.element( element[ 0 ].querySelector( 'ng-include' ) ); ngInclude.replaceWith( ngInclude.children() );
— Mattijs
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.