Zastąpić węzeł ng-include szablonem?

85

Coś nowego w kanciastym. Czy można zamienić węzeł ng-include na zawartość dołączonego szablonu? Na przykład z:

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

Wygenerowany html to:

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

Ale ja chcę:

<div ng-app>
    <script type="text/ng-template" id="test.html">
        <p>Test</p>
    </script>
    <p>Test</p>
</div>
SunnySydeUp
źródło
1
usuń pojedyncze cudzysłowy z 'test.html'szablonu zamiast
adresu
1
czytając komentarze w dokumencie dla ng-include, wydaje się, że otacza on ciąg pojedynczymi cudzysłowami dla szablonu i bez adresu URL. Również powiązane pytanie dotyczące
przepełnienia stosu
czytasz dokumenty i publikujesz linki do nich wstecz
charlietfl

Odpowiedzi:

134

Miałem ten sam problem i nadal chciałem, aby funkcje ng-include zawierały dynamiczny szablon. Budowałem dynamiczny pasek narzędzi Bootstrap i potrzebowałem czystszego znacznika, aby style CSS były poprawnie stosowane.

Oto rozwiązanie, które wymyśliłem dla zainteresowanych:

HTML:

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

Dyrektywa celna:

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

Gdyby to rozwiązanie zostało użyte w powyższym przykładzie, ustawienie scope.dynamicTemplatePath na „test.html” spowodowałoby uzyskanie żądanego znacznika.

Brady Isom
źródło
Wymaga wersji kątowej 1.2 +
colllin
Odwołałem się do
Peter V. Mørch
Działa to dla kątowej 1.2.5+. W przypadku wersji 1.2.4, jeśli masz jedno ng-include, że ng-zawiera inne, nie powiedzie się. Zgaduję z powodu numeru 5247 , ale nie jestem pewien. Zobacz Changelog dla siebie. Oto Plunkr demonstrujący ten problem w 1.2.4 (zmień na kątowy 1.2.5 i zobacz, jak działa! :-)
Peter V. Mørch
9
Zwróć uwagę, że taka manipulacja DOM jest dość hakerska. Istnieje problem, jeśli element główny dołączonego szablonu używa czegoś takiego jak ng-repeat. Nie będzie w stanie wstawić wyników do DOM.
Guria
1
Zobacz moją odpowiedź na to pytanie, to się nie powiedzie, ponieważ funkcja prelink będzie już działała w elementach potomnych.
Sai Dubbaka
28

Dzięki @ user1737909 zdałem sobie sprawę, że ng-include nie jest właściwą drogą. Dyrektywy są lepszym podejściem i są bardziej jednoznaczne.

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

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

W html:

<blah></blah>
SunnySydeUp
źródło
2
dzięki! szukałem rozwiązania zawierającego ng, ale to pomogło mi uświadomić sobie, że dyrektywy są lepsze
Matt Kim,
Pamiętaj, że replace:truew szablonach jest oznaczony do wycofania . Unikałbym korzystania z tego rozwiązania ze względu na stan wycofania.
Peter V. Mørch
@ PeterV.Mørch Thanks. Dla zainteresowanych jest to zobowiązanie: github.com/angular/angular.js/commit/… . Wygląda na to, że został wycofany ze względu na swoją złożoność (i być może z innych powodów).
SunnySydeUp
15

Miałem ten sam problem, mój arkusz stylów CSS innej firmy nie podobał się dodatkowemu elementowi DOM.

Moje rozwiązanie było super proste. Po prostu przesuń ng-include 1 w górę. Więc zamiast

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

Po prostu zrobiłem:

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

Założę się, że to zadziała w większości sytuacji, nawet jeśli technicznie nie jest to, o co chodzi w pytaniu.

xeor
źródło
10

Inną alternatywą jest napisanie własnej prostej dyrektywy zamień / dołącz, np

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

Zostałoby to następnie użyte w następujący sposób:

<div my-replace="test.html"></div>
Daniel Egan
źródło
9

To jest właściwy sposób zastępowania dzieci

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
źródło
4
Mój dołączony częściowy kod HTML ma kilka powtórzeń ng i jest to jedyna odpowiedź, która je rozwiązuje! Wielkie dzięki.
Saad Benbouzid
Musiałem przenieść zawartość funkcji z compiledo link, ponieważ mój element był pusty na etapie kompilacji.
itachi
3

Poniższa dyrektywa rozszerza natywną funkcjonalność dyrektywy ng-include.

Dodaje detektor zdarzeń, aby zastąpić oryginalny element, gdy zawartość jest gotowa i załadowana.

Użyj go w oryginalny sposób, po prostu dodaj atrybut „zamień”:

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

lub z notacją atrybutu:

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

Oto dyrektywa (pamiętaj, aby uwzględnić moduł „include-replace” jako zależność):

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
źródło
2

Wybrałbym bezpieczniejsze rozwiązanie niż to dostarczone przez @Brady Isom.

Wolę polegać na onloadopcji podanej przez, ng-includeaby upewnić się, że szablon jest załadowany przed próbą jego usunięcia.

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

Nie ma potrzeby stosowania drugiej dyrektywy, ponieważ wszystko jest załatwiane w ramach pierwszej.

Masadow
źródło
uwaga, w przypadku angular 1.5 pierwszym dzieckiem w elemencie dyrektywy jest komentarz. Tak wykonane, na pewno dostanę element ng obejmują a następnie zastąpić go jego dzieci: let ngInclude = angular.element( element[ 0 ].querySelector( 'ng-include' ) ); ngInclude.replaceWith( ngInclude.children() );
Mattijs