Избегайте использования дополнительных узлов 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);