Директива AngularJS для анализа и замены содержимого пользовательских элементов
Я хотел бы создать простую директиву markdown, которая принимает некоторый контент внутри элемента, анализирует его и заменяет его html.
Итак, это:
<markdown>#Heading</markdown>
или (где $scope.heading = '#Heading';)
<markdown>{{heading}}</markdown>
Становится следующим:
<h1>Heading</h1>
Моя директива пока (очевидно, не завершена!):
.directive('markdown', function () {
return {
restrict: 'E',
replace: true,
link: function ($scope, $element, $attrs) {
// Grab contents
var contents = /* How do I do this? */
var newContents = Markdowner.transform(contents);
// Replace <markdown> element with newContents
/* How do I do this? */
}
}
})
Я не уверен, как захватить содержимое директивы? Мне нужно скомпилировать его?!
Анализ разметки - это просто пример
Ответы
Ответ 1
Здесь вы идете!
Рабочий демонстрационный пример
app.directive('markdown', function() {
return {
restrict: 'E',
transclude: true,
compile: function(elem) {
elem.replaceWith(Markdowner.transform(elem.html()));
}
}
});
Ответ 2
ngTransclude специально разработан для этого.
myModule.directive('heading', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: true,
template: '<h1 ng-transclude></h1>'
};
}
Затем используйте его следующим образом:
<heading><span>{{foo}}</span></heading>
Здесь работает fiddle (angular 1.2.7).
Кроме того, я предполагаю, что вам нужна какая-то интеграция с уценкой. Ниже приведена версия, использующая transclude, чтобы вы получили контейнер div
.
Этот пропускает все поведение transclude, и я предполагаю, что это ближе к тому, что вы после.
Ответ 3
Вы можете получить и установить скомпилированное содержимое элемента в функции ссылок, используя:
element.html() //get
element.html("blah") //set
Вот пример, основанный на примере Sergiu ниже, который обрабатывает привязки, содержащиеся в html, используя scope.$eval()
, перед вызовом конвертора меток:
http://jsfiddle.net/edeustace/4rE85/1/
angular.module('transclude', [])
.directive('markdown', function() {
var regex = /\{\{(.*?)\}\}/;
var converter = new Showdown.converter();
return {
restrict: 'E',
replace: true,
scope: true,
link: function (scope, element) {
var processTemplate = function(text){
var results = text.match(regex);
if(!results){
return text;
} else {
var value = scope.$eval(results[1]);
var replaceKey = new RegExp("{{" + results[1] + "}}","g");
text = text.replace(replaceKey, value);
return processTemplate(text);
}
};
var text = element.text();
var processed = processTemplate(text);
var markdownText = converter.makeHtml(processed);
element.html(markdownText);
}
};
});
который будет работать с:
<markdown>
# Bar {{foo}} {{foo}}
# {{bing}}
</markdown>
Или вы можете привязать его к атрибуту, который затем можно использовать в своей директиве:
app.directive('markdownWithBinding', function () {
var converter = new Showdown.converter();
return {
restrict: 'E',
scope: {
'md' : '@'
},
link: function ($scope, $element, $attrs) {
$scope.$watch('md', function(newMd){
var markdownText = converter.makeHtml(newMd);
element.html(markdownText);
});
}
}
});
Используется так:
<markdown-with-binding md="Hello {{name}}"></markdown-with-binding>
<!-- outputs Hello World!!! -->
Старый ответ
Это произойдет в link(), который предназначен для связывания области с элементом. Для структурных изменений, когда не требуется никаких областей, вам может быть лучше сделать изменения в функции компиляции:
app.directive('markdown', function () {
var link = function ($scope, $element, $attrs) {};
return {
restrict: 'E',
replace: true,
compile: function($element, $attrs, $transclude){
if($element.html() == "#Hello"){
$element.html("<h1>Hello</h1>");
}
return link;
},
}
});
Здесь отличный учебник по компонентам: http://www.youtube.com/watch?v=A6wq16Ow5Ec
Ответ 4
В вашей функции связи AngularJS уже проанализировал ваш HTML-код и заменил содержимое вашим шаблоном (который в вашем случае отсутствует, так как вы "замените" на true).
Вы можете захватить внутреннее содержимое html из элемента $, который является элементом jQuery (jQLite).
var contents = $element.innerHTML;