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.