Substituir o nó ng-include pelo modelo?

85

Meio novo no angular. É possível substituir o nó ng-include pelo conteúdo do modelo incluído? Por exemplo, com:

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

O html gerado é:

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

Mas o que eu quero é:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <p>Test</p>
</div>
SunnySydeUp
fonte
1
remova aspas simples de 'test.html'para usar o modelo em vez do url
charlietfl
1
lendo os comentários do documento para ng-include, parece que está cercando a string com aspas simples para o modelo e sem para o url. Além disso, questão de stackoverflow
SunnySydeUp
você está lendo documentos e
postou seu link

Respostas:

134

Eu tive o mesmo problema e ainda queria que os recursos do ng-include incluíssem um modelo dinâmico. Eu estava construindo uma barra de ferramentas dinâmica Bootstrap e precisava de uma marcação mais limpa para que os estilos CSS fossem aplicados corretamente.

Aqui está a solução que encontrei para aqueles que estão interessados:

HTML:

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

Diretiva Customizada:

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

Se essa solução fosse usada no exemplo acima, definir scope.dynamicTemplatePath como 'test.html' resultaria na marcação desejada.

Brady Isom
fonte
Requer angular v1.2 +
colllin de
Referenciei esta resposta em uma pergunta semelhante: angularjs - Evite usar nós DOM extras ao usar nginclude - Stack Overflow
Peter V. Mørch
Isso funciona para o angular 1.2.5+. Para 1.2.4, se você tiver um ng-include que ng-inclui outro, ele falhará. Suponho que seja por causa do número 5247 , mas não tenho certeza. Veja o Changelog por si mesmo. Aqui está um Plunkr demonstrando este problema com 1.2.4 (mude para angular 1.2.5 e veja se funciona! :-)
Peter V. Mørch
9
Observe que essa manipulação de DOM é bastante hacky. Há um problema se o elemento raiz do modelo incluído usa algo como ng-repeat. Não será possível inserir resultados no DOM.
Guria
1
Por favor, veja minha resposta para isso, isso falhará porque a função de pré-link já terá sido executada em elementos filho.
Sai Dubbaka
28

Então, graças a @ user1737909, percebi que ng-include não é a melhor opção. As diretivas são a melhor abordagem e mais explícitas.

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

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

Em html:

<blah></blah>
SunnySydeUp
fonte
2
obrigado! estava procurando uma solução ng-include, mas isso me ajudou a perceber que as diretivas são melhores
Matt Kim
Lembre-se de que replace:trueem modelos está marcado para suspensão de uso . Eu evitaria usar esta solução devido ao status de suspensão de uso.
Peter V. Mørch
@ PeterV.Mørch Obrigado. Para os interessados, este é o commit: github.com/angular/angular.js/commit/… . Parece que foi descontinuado devido à sua complexidade (e talvez por outros motivos).
SunnySydeUp
15

Eu tive o mesmo problema, minha folha de estilo css de terceiros não gostou do elemento DOM extra.

Minha solução foi super simples. Basta mover o ng-include 1 para cima. Então, em vez 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>

Eu simplesmente fiz:

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

Aposto que isso funcionará na maioria das situações, mesmo que tecnicamente não seja o que a pergunta está perguntando.

xeor
fonte
10

Outra alternativa é escrever sua própria diretiva substituir / incluir simples, por exemplo

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

Isso seria então usado da seguinte maneira:

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

Esta é a maneira correta de substituir os filhos

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
fonte
4
Meu html parcial incluído tem algumas repetições de ng, e esta é a única resposta que os resolve! Muito obrigado.
Saad Benbouzid
Tive que mover o conteúdo da função de compilepara link, porque meu elemento estava vazio durante o estágio de compilação.
itachi
3

A diretiva a seguir estende a funcionalidade da diretiva nativa ng-include.

Ele adiciona um ouvinte de evento para substituir o elemento original quando o conteúdo está pronto e carregado.

Use-o da maneira original, basta adicionar o atributo "substituir":

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

ou com notação de atributo:

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

Aqui está a diretiva (lembre-se de incluir o módulo 'incluir-substituir' como dependência):

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

Eu escolheria uma solução mais segura do que a fornecida por @Brady Isom.

Prefiro contar com a onloadopção fornecida por ng-includepara garantir que o modelo seja carregado antes de tentar removê-lo.

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

Não há necessidade de uma segunda diretriz, pois tudo é tratado dentro da primeira.

Masadow
fonte
cuidado, pois com o angular 1.5, o primeiro filho no elemento de diretiva é um comentário. Portanto, certifiquei-me de obter o elemento ng-include e, em seguida, substituí-lo por seus filhos: let ngInclude = angular.element( element[ 0 ].querySelector( 'ng-include' ) ); ngInclude.replaceWith( ngInclude.children() );
Mattijs