Избегайте использования дополнительных узлов DOM при использовании nginclude

Я изо всех сил пытаюсь задуматься о том, как включить ng-include, не использовать дополнительный элемент DOM, поскольку я создаю приложение angular из простой демонстрации HTML. Я работаю с довольно тонким HTML с полностью разработанным, тесно связанным с DOM CSS (построен из SASS), и рефакторинг - это то, чего я хочу избежать любой ценой.

Вот фактический код:

<div id="wrapper">
    <header
        ng-controller="HeaderController"
        data-ng-class="headerType"
        data-ng-include="'/templates/base/header.html'">
    </header>
    <section
        ng-controller="SubheaderController"
        data-ng-class="subheaderClass"
        ng-repeat="subheader in subheaders"
        data-ng-include="'/templates/base/subheader.html'">
    </section>
    <div
        class="main"
        data-ng-class="mainClass"
        data-ng-view>
    </div>
</div>

Мне нужно <section> быть повторяющимся элементом, но иметь свою собственную логику и различный контент. Оба, содержание и количество повторений зависят от бизнес-логики. Как вы можете видеть, поместите ng-контроллер и ng-repeat в <section> элемент не будет работать. Что бы было, однако, вставить новый DOM node, чего я пытаюсь избежать.

Что я упускаю? Это лучшая практика или есть лучший способ?


РЕДАКТИРОВАТЬ: просто чтобы уточнить, как указано в комментариях, окончательный HTML, который я пытаюсь создать, будет следующим:

<div id="wrapper">
    <header>...</header>
    <section class="submenuX">
        some content from controller A and template B (e.g. <ul>...</ul>)
    </section>
    <section class="submenuY">
        different content from same controller A and template B (e.g. <div>...</div>)
    </section>
    <section class="submenuZ">
        ... (number of repetitions is defined in controller A e.g. through some service)
    </section>

    <div>...</div>
</div>

Причина, по которой я хочу использовать тот же шаблон B (subheader.html), предназначена для чистоты кода. Я заставляю subheader.html иметь какой-то ng-переключатель, чтобы возвращать динамический контент.

Но в основном подстилающий quiestion: есть ли способ включать содержимое шаблона прозрачно, без использования DOM node?


EDIT2. Решение должно быть повторно использовано. =)

Ответ 1

Некоторые из других ответов предлагают replace:true, но имейте в виду, что replace:true в шаблонах отмечен для устаревания.

Вместо этого в ответе на аналогичный вопрос, мы найдем альтернативу: он позволяет вам написать:

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

Пользовательская директива:

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

(отменить ответ от другого ответа)

Ответ 2

Изменить: После некоторых исследований и для полноты, я добавил некоторую информацию. Начиная с 1.1.4, следующие работы:

app.directive('include',
    function () {
        return {
            replace: true,
            restrict: 'A',
            templateUrl: function (element, attr) {
                return attr.pfInclude;
            }
        };
    }
);

Использование:

<div include="'path/to/my/template.html'"></div>

Существует, однако, одно: шаблон не может быть динамическим (как, передавая переменную через область видимости, потому что $scope или любой DI в этом случае недоступен в templateUrl - см. этот вопрос), может передаваться только строка (как и html-фрагмент выше). Чтобы обойти эту конкретную проблему, этот фрагмент кода должен сделать трюк (kudos to этот плункер):

app.directive("include", function ($http, $templateCache, $compile) {
    return {
        restrict: 'A',
        link: function (scope, element, attributes) {
            var templateUrl = scope.$eval(attributes.include);
            $http.get(templateUrl, {cache: $templateCache}).success(
                function (tplContent) {
                    element.replaceWith($compile(tplContent.data)(scope));
                }
            );
        }
    };
});

Использование:

<div include="myTplVariable"></div>

Ответ 3

Вы можете создать настраиваемую директиву, связав ее с шаблоном с свойством templateUrl и установив replace в true:

app.directive('myDirective', function() {
  return {
    templateUrl: 'url/to/template',
    replace: true,
    link: function(scope, elem, attrs) {

    }
  }
});

Это будет включать шаблон как есть, без какого-либо элемента оболочки, без какой-либо области оболочки.

Ответ 4

Для тех, кто посещает этот вопрос:

Как и для angular 1.1.4+, вы можете использовать функцию в шаблонеURL, чтобы сделать ее динамической.

Проверьте этот другой ответ здесь

Ответ 5

При правильной настройке вы можете определить свою собственную директиву ngInclude, которая может запускаться вместо той, которая предоставляется Angular.js, и запретить выполнение встроенной директивы когда-либо.

Чтобы предотвратить выполнение директивы Angular -built-in от выполнения, важно установить приоритет вашей директивы выше, чем у встроенной директивы (400 для ngInclude) и установить для свойства terminal значение true.

После этого вам необходимо предоставить функцию пост-ссылки, которая извлекает шаблон и заменяет элемент DOM node на скомпилированный шаблон HTML.

Слово предупреждения: это довольно драконов, вы переопределяете поведение ngInclude для всего вашего приложения. Поэтому я устанавливаю директиву ниже не на myApp, а внутри одной из моих собственных директив, чтобы ограничить ее область действия. Если вы хотите использовать его в масштабе всего приложения, вы можете настроить его поведение, например. замените элемент только в том случае, если в HTML установлен атрибут replace, а по умолчанию - установка innerHtml.

Также: это может плохо отразиться на анимации. Код исходного ngInclude -directive длиннее, поэтому, если вы используете анимацию в своем приложении, c & p исходный код и shoehorn `$element.replaceWith() в это.

var includeDirective = ['$http', '$templateCache', '$sce', '$compile',
                        function($http, $templateCache, $sce, $compile) {
    return {
        restrict: 'ECA',
        priority: 600,
        terminal: true,
        link: function(scope, $element, $attr) {
            scope.$watch($sce.parseAsResourceUrl($attr.src), function ngIncludeWatchAction(src) {    
                if (src) {
                    $http.get(src, {cache: $templateCache}).success(function(response) {
                        var e =$compile(response)(scope);
                        $element.replaceWith(e);
                    });       
                }
            }); 
        }
    };
}];

myApp.directive('ngInclude', includeDirective);