I'm not sure the title is even correct. What I'm doing is a little over my head as I'm fairly new to Angular.
I'm trying to have a directive run which comes from a bound value. The directive returns HTML. My problem is trying to get that HTML to display. I think I'm missing the concept of transclude (as I'm not using it), I'm all screwed up.
HERE IS A PLUNK: http://plnkr.co/edit/fRhpsS97IGbTWtDPCMlI?p=preview
I have data in my scope that looks like this:
$scope.data = [{
details: 'IUt euismod tellus, at posuere sem. <code lang="java">int foo = 21;</code> Nullam mattis ac elit in gravida.'
},
{
details: 'BHA BHA BHA e sem. <code lang="java">int wutWut = 56;</code> Nullam mattis ac elit in gravida. Sed nec ipsum sed urna.'
}
];
I'm displaying the data like this:
<pre ng-repeat="item in data">
{{item.details}}
</pre>
That all works fine, but the problem is that I need to have the <code>
tags (from item.details) processed as a directive. Said directive should read the lang
attribute and properly highlight the contents of the element with the Sunlight Syntax Highlighter which is included.
So here's the directive for <code>
.directive('code', ['$filter',
function($filter) {
return {
restrict: 'E',
replace: true,
scope: {
lang: '@'
},
controller: ['$scope','$element',
function($scope, $element) {
// default lang
if (typeof $scope.lang === 'undefined')
$scope.lang = 'java';
$scope.src = $element.html();
}
],
template: '<span>{{src | codeHighlight:lang}}</span>'
};
}
]);
which basically just passes the the bits to the codeHighlight
filter. The reason I pass it to a filter is because there's another point in my application where the highlighting method is done without a <code>
tag so it works well for what I'm doing (minimal code repeat). I'm thinking I'm supposed to use the transclude option and the $filter service but I'm not 100% sure how to get the value between the tags.
Here's my codeHighlight
filter. This does the highlighting using the Sunlight lib. So it returns HTML as a string. When I use it directly I usually do something like this: ng-bind-html="foo.bar | codeHighlight | safeHtml"
.filter('codeHighlight', function() {
return function(code, lang) {
// Default lang
if (typeof lang === 'undefined')
lang = 'java';
// Grab a highlighter
var highlighter = new Sunlight.Highlighter();
var context = highlighter.highlight(code, lang);
var el = angular.element(context.getNodes());
var html = el.wrap('<p/>').parent().html();
// Convert nbsp's to actual spaces (so line breaks work better)
html = html.replace(/ /gi, ' ');
return html;
};
})
Here's the safeHtml
filter just for sanity, but doesn't really apply to my question
.filter('safeHtml', ['$sce',function($sce){
return function(html){
return $sce.trustAsHtml(html);
};
}])
So all that would work, except the original data.details is never processed because Angular doesn't process (What's the right word? I know it's not process) the values spit to the screen (for obvious reasons)
So I modify the output and create a directive to recompile the output value. I use the compile attribute because I don't understand how to properly pull the linked? data from between the tags.
<pre ng-repeat="item in data" compile="item.details"></pre>
And here's the compile
directive
.directive('compile', ['$compile',
function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch(attrs.compile, function(html) {
element.html(html);
$compile(element.contents())(scope);
});
}
}
}
])
So now everything works!!! neato! except my html is displayed as a string because Angular sanitizes output. Unfortunately the only way I know how to print raw HTML to the page is with the ng-bind-html
directive which I can't do because I'm using my stupid compile
directive.
I thought maybe in the watch function I could append element.html(element.html())
after the $compile
function, but that just returns the raw template.
Obviously I'm stepping into a realm that I don't completely understand.
My dream would be to have the pre tag setup like in the first example:
<pre ng-repeat="item in data" compile="item.details">
{{item.details}}
</pre>
as it kinda looks proper, but I'm begging here not choosing. If anyone could help me even a little I would be extremely grateful. I'm completely stuck and I don't even know what to Google anymore.