¿Reemplazar el nodo ng-include con la plantilla?

85

Un poco nuevo en angular. ¿Es posible reemplazar el nodo ng-include con el contenido de la plantilla incluida? Por ejemplo, con:

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

El html generado es:

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

Pero lo que quiero es:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <p>Test</p>
</div>
SunnySydeUp
fuente
1
elimine las comillas simples de 'test.html'para usar la plantilla en lugar de la URL
charlietfl
1
Al leer los comentarios del documento para ng-include, parece que está rodeando la cadena con comillas simples para la plantilla y sin la url. Además, pregunta
SunnySydeUp
está leyendo documentos y publicaciones que vinculó hacia atrás
charlietfl

Respuestas:

134

Tuve este mismo problema y todavía quería que las características de ng-include incluyeran una plantilla dinámica. Estaba creando una barra de herramientas dinámica de Bootstrap y necesitaba el marcado más limpio para que los estilos CSS se aplicaran correctamente.

Aquí está la solución que se me ocurrió para aquellos que estén interesados:

HTML:

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

Directiva personalizada:

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

Si esta solución se usara en el ejemplo anterior, establecer scope.dynamicTemplatePath en 'test.html' daría como resultado el marcado deseado.

Brady Isom
fuente
Requiere angular v1.2 +
colllin
Hice referencia a esta respuesta en una pregunta similar: angularjs - Evite el uso de nodos DOM adicionales cuando use nginclude - Stack Overflow
Peter V. Mørch
Esto funciona para angular 1.2.5+. Para 1.2.4, si tiene un ng-include que ng-includes otro, falla. Supongo que se debe al número 5247 , pero no estoy seguro. Consulte el registro de cambios usted mismo. Aquí hay un Plunkr que demuestra este problema con 1.2.4 (¡cambie a angular 1.2.5 y vea cómo funciona! :-)
Peter V. Mørch
9
Tenga en cuenta que dicha manipulación DOM es bastante complicada. Hay un problema si el elemento raíz de la plantilla incluida usa algo como ng-repeat. No podrá insertar resultados en DOM.
Guria
1
Por favor, vea mi respuesta a esto, esto fallará porque la función de prevínculo ya se habrá ejecutado en elementos secundarios.
Sai Dubbaka
28

Entonces, gracias a @ user1737909, me di cuenta de que ng-include no es el camino a seguir. Las directivas son el mejor enfoque y son más explícitas.

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

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

En html:

<blah></blah>
SunnySydeUp
fuente
2
¡Gracias! estaba buscando una solución ng-include, pero esto me ayudó a darme cuenta de que las directivas son mejores
Matt Kim
Tenga en cuenta que replace:trueen las plantillas está marcado como obsoleto . Evitaría usar esta solución debido al estado de desaprobación.
Peter V. Mørch
@ PeterV.Mørch Gracias. Para los interesados, este es el compromiso: github.com/angular/angular.js/commit/… . Parece que fue desaprobado por su complejidad (y quizás por otras razones).
SunnySydeUp
15

Tuve el mismo problema, a mi hoja de estilo CSS de terceros no le gustó el elemento DOM adicional.

Mi solución fue súper simple. Simplemente mueva el ng-include 1 hacia arriba. Entonces en lugar de

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

Simplemente hice:

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

Apuesto a que esto funcionará en la mayoría de las situaciones, aunque técnicamente no es lo que la pregunta hace.

xeor
fuente
10

Otra alternativa es escribir su propia directiva simple de reemplazo / inclusión, por ejemplo

    .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;
                   }
               };
           });

Esto luego se usaría de la siguiente manera:

<div my-replace="test.html"></div>
Daniel Egan
fuente
9

Esta es la forma correcta de reemplazar a los niños.

angular.module('common').directive('includeReplace', function () {
    return {
        require: 'ngInclude',
        restrict: 'A',
        compile: function (tElement, tAttrs) {
            tElement.replaceWith(tElement.children());
            return {
                post : angular.noop
            };
        }
    };
});
Sai Dubbaka
fuente
4
Mi html parcial incluido obtuvo algo de repetición ng, ¡y esta es la única respuesta que los resuelve! Muchas gracias.
Saad Benbouzid
Tuve que mover el contenido de la función de compilea link, porque mi elemento estaba vacío durante la etapa de compilación.
itachi
3

La siguiente directiva amplía la funcionalidad de la directiva nativa ng-include.

Agrega un detector de eventos para reemplazar el elemento original cuando el contenido está listo y cargado.

Úselo de la forma original, simplemente agregue el atributo "reemplazar":

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

o con notación de atributo:

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

Aquí está la directiva (recuerde incluir el módulo 'incluir-reemplazar' como dependencia):

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();
                    };
                });
            }
        }
    };
});
Edrian
fuente
2

Yo optaría por una solución más segura que la proporcionada por @Brady Isom.

Prefiero confiar en la onloadopción dada por ng-includepara asegurarme de que la plantilla esté cargada antes de intentar eliminarla.

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

No es necesaria una segunda directiva ya que todo se maneja dentro de la primera.

Masadow
fuente
tenga en cuenta que con angular 1.5 el primer hijo en el elemento directivo es un comentario. Así que me aseguré de obtener el elemento ng-include y luego reemplazarlo con sus hijos: let ngInclude = angular.element( element[ 0 ].querySelector( 'ng-include' ) ); ngInclude.replaceWith( ngInclude.children() );
Mattijs