Remplacer le nœud ng-include par un modèle?

85

Un peu nouveau à angulaire. Est-il possible de remplacer le nœud ng-include par le contenu du modèle inclus? Par exemple, avec:

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

Le html généré est:

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

Mais ce que je veux c'est:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <p>Test</p>
</div>
SunnySydeUp
la source
1
supprimer les guillemets simples de 'test.html'pour utiliser le modèle plutôt que l'url
charlietfl
1
en lisant les commentaires de la documentation pour ng-include, il semble qu'il entoure la chaîne de guillemets simples pour le modèle et sans pour l'url. En outre, question
SunnySydeUp
vous lisez des documents et postez un lien vers l'arrière
charlietfl

Réponses:

134

J'ai eu ce même problème et je voulais toujours que les fonctionnalités de ng-include incluent un modèle dynamique. Je construisais une barre d'outils Bootstrap dynamique et j'avais besoin d'un balisage plus propre pour que les styles CSS soient appliqués correctement.

Voici la solution que j'ai proposée pour ceux qui sont intéressés:

HTML:

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

Directive douanière:

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

Si cette solution était utilisée dans l'exemple ci-dessus, la définition de scope.dynamicTemplatePath sur «test.html» entraînerait le balisage souhaité.

Brady Isom
la source
Nécessite angular v1.2 +
colllin
J'ai fait référence à cette réponse dans une question similaire: angularjs - Évitez d'utiliser des nœuds DOM supplémentaires lors de l'utilisation de nginclude - Stack Overflow
Peter V.Mørch
Cela fonctionne pour angular 1.2.5+. Pour 1.2.4, si vous avez un ng-include que ng-en inclut un autre, cela échoue. Je suppose à cause de # 5247 , mais je ne suis pas sûr. Voir Changelog par vous-même. Voici un Plunkr démontrant ce problème avec 1.2.4 ( passez à angulaire 1.2.5 et voyez que ça marche! :-)
Peter V. Mørch
9
Notez qu'une telle manipulation DOM est assez hacky.Il y a un problème si l'élément racine du modèle inclus utilise quelque chose comme ng-repeat. Il ne pourra pas insérer de résultats dans DOM.
Guria
1
S'il vous plaît voir ma réponse à cela, cela échouera car la fonction prelink sera déjà exécutée dans les éléments enfants.
Sai Dubbaka
28

Donc, grâce à @ user1737909, j'ai réalisé que ng-include n'est pas la voie à suivre. Les directives sont la meilleure approche et la plus explicite.

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

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

En html:

<blah></blah>
SunnySydeUp
la source
2
Merci! cherchait une solution ng-include mais cela m'a aidé à réaliser que les directives sont meilleures
Matt Kim
Gardez à l'esprit que replace:truedans les modèles est marqué comme obsolète . J'éviterais d'utiliser cette solution en raison du statut d'obsolescence.
Peter V.Mørch
@ PeterV.Mørch Merci. Pour ceux qui sont intéressés, voici le commit: github.com/angular/angular.js/commit/… . Il semble qu'il soit obsolète en raison de sa complexité (et peut-être d'autres raisons).
SunnySydeUp
15

J'ai eu le même problème, ma feuille de style css tierce n'aimait pas l'élément DOM supplémentaire.

Ma solution était super simple. Déplacez simplement le ng-include 1 vers le haut. Donc au lieu 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>

J'ai simplement fait:

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

Je parie que cela fonctionnera dans la plupart des situations, même si ce n'est techniquement pas ce que la question pose.

xeor
la source
10

Une autre alternative consiste à écrire votre propre directive replace / include simple, par exemple

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

Cela serait alors utilisé comme suit:

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

C'est la bonne façon de remplacer les enfants

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
la source
4
Mon html partiel inclus a obtenu des ng-repeat, et c'est la seule réponse à les résoudre! Merci beaucoup.
Saad Benbouzid
J'ai dû déplacer le contenu de la fonction de compilevers link, car mon élément était vide pendant la phase de compilation.
itachi
3

La directive suivante étend la fonctionnalité de la directive native ng-include.

Il ajoute un écouteur d'événements pour remplacer l'élément d'origine lorsque le contenu est prêt et chargé.

Utilisez-le de la manière originale, ajoutez simplement l'attribut "remplacer":

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

ou avec notation attributaire:

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

Voici la directive (n'oubliez pas d'inclure le module 'include-replace' comme dépendance):

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
la source
2

J'irais avec une solution plus sûre que celle fournie par @Brady Isom.

Je préfère me fier à l' onloadoption donnée par ng-includepour m'assurer que le modèle est chargé avant d'essayer de le supprimer.

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

Pas besoin d'une deuxième directive puisque tout est géré dans la première.

Masadow
la source
attention, avec angular 1.5, le premier enfant de l'élément directive est un commentaire. Je me suis donc assuré d'obtenir l'élément ng-include, puis de le remplacer par ses enfants: let ngInclude = angular.element( element[ 0 ].querySelector( 'ng-include' ) ); ngInclude.replaceWith( ngInclude.children() );
Mattijs