Reputation: 31378
I would like to create a simple markdown directive that accepts some content within the element, parses it and replaces it with html.
So this:
<markdown>#Heading</markdown>
or this (where $scope.heading = '#Heading';)
<markdown>{{heading}}</markdown>
Becomes this:
<h1>Heading</h1>
My directive so far (obviously not complete!):
.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? */
}
}
})
I'm unsure of how to grab the contents of the directive? Would I need to compile it?!
Parsing Markdown is just an example
Upvotes: 10
Views: 22071
Reputation: 75640
Here you go!
app.directive('markdown', function() {
return {
restrict: 'E',
transclude: true,
compile: function(elem) {
elem.replaceWith(Markdowner.transform(elem.html()));
}
}
});
Upvotes: 8
Reputation: 2706
You can get and set the compiled contents of the element in the link function using:
element.html() //get
element.html("blah") //set
Here is a sample based on Sergiu's sample below that processes the bindings contained within the html using scope.$eval()
, before calling the markdown converter:
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);
}
};
});
which will work with:
<markdown>
# Bar {{foo}} {{foo}}
# {{bing}}
</markdown>
Or you can bind it to an attribute that you can then use in your directive:
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);
});
}
}
});
Used like so:
<markdown-with-binding md="Hello {{name}}"></markdown-with-binding>
<!-- outputs Hello World!!! -->
This will happen in link() which is for linking the scope to the element. For structural changes where no scope is required you may be better off making your changes in the compile function:
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;
},
}
});
Here's a great tutorial on components: http://www.youtube.com/watch?v=A6wq16Ow5Ec
Upvotes: 3
Reputation: 10153
ngTransclude is specifically designed for this.
myModule.directive('heading', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: true,
template: '<h1 ng-transclude></h1>'
};
}
Then use it like this:
<heading><span>{{foo}}</span></heading>
Here's a working fiddle (angular 1.2.7).
Also, I'm guessing you need some sort of markdown integration. Here's a version using transclude so that you end up with a div
container.
This one skips the whole transclude behavior and I'm guessing it's closer to what you're after.
Upvotes: 6
Reputation: 1712
On your link function, AngularJS already parsed your HTML and replaced the contents by your template (which in your case is missing, since you have "replace" set to true).
You can grab the inner html content from the $element, which is a jQuery (jQLite) element.
var contents = $element.innerHTML;
Upvotes: 0