angular ng-bind-html and directive within it
Solution 1
I was also facing this problem and after hours searching the internet I read @Chandermani's comment, which proved to be the solution. You need to call a 'compile' directive with this pattern:
HTML:
<div compile="details"></div>
JS:
.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}])
You can see a working fiddle of it here
Solution 2
Thanks for the great answer vkammerer. One optimization I would recommend is un-watching after the compilation runs once. The $eval within the watch expression could have performance implications.
angular.module('vkApp')
.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) {
var ensureCompileRunsOnce = scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
// Use un-watch feature to ensure compilation happens only once.
ensureCompileRunsOnce();
}
);
};
}]);
Here's a forked and updated fiddle.
Solution 3
Add this directive angular-bind-html-compile
.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
// Incase value is a TrustedValueHolderType, sometimes it
// needs to be explicitly called into a string in order to
// get the HTML string.
element.html(value && value.toString());
// If scope is provided use it, otherwise use parent scope
var compileScope = scope;
if (attrs.bindHtmlScope) {
compileScope = scope.$eval(attrs.bindHtmlScope);
}
$compile(element.contents())(compileScope);
});
}
};
}]);
Use it like this :
<div bind-html-compile="data.content"></div>
Really easy :)
Solution 4
Unfortunately I don't have enough reputation to comment.
I could not get this to work for ages. I modified my ng-bind-html
code to use this custom directive, but I failed to remove the $scope.html = $sce.trustAsHtml($scope.html)
that was required for ng-bind-html to work. As soon as I removed this, the compile function started to work.
Solution 5
For anyone dealing with content that has already been run through $sce.trustAsHtml
here is what I had to do differently
function(scope, element, attrs) {
var ensureCompileRunsOnce = scope.$watch(function(scope) {
return $sce.parseAsHtml(attrs.compile)(scope);
},
function(value) {
// when the parsed expression changes assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current scope.
$compile(element.contents())(scope);
// Use un-watch feature to ensure compilation happens only once.
ensureCompileRunsOnce();
});
}
This is only the link
portion of the directive as I'm using a different layout. You will need to inject the $sce
service as well as $compile
.
Amitava
I'm Javascript developer with over 8 years professional experience building full stack web applications. My strength is in Node.Js backend and React, Redux ecosystem for UI. I have 8+ years experience in NodeJs & I've been building React apps since last 4+ years.
Updated on April 26, 2020Comments
-
Amitava about 4 years
I have a element which I would like to bind html to it.
<div ng-bind-html="details" upper></div>
That works. Now, along with it I also have a directive which is bound to the bound html:
$scope.details = 'Success! <a href="#/details/12" upper>details</a>'
But the directive
upper
with the div and anchor do not evaluate. How do I make it work? -
spaffy over 9 yearsIn line #2, ie.
function(scope, element, attrs)
, where did you get from those three arguments, scope, element and attrs? -
Ben about 9 years@spaffy - they're part of Angular framework's signature for the
link
property. They'll be passed automatically each time whenlink
is called by the Angular framework. They'll always be available. -
Sanyam Jain about 9 yearsCan i have the vice versa for it?
-
foozhan about 9 yearsthis is not work in response of ajax but accepted answer work
-
Phil Nicholas over 8 yearsWarning: The fiddle for this answer works, but the
.directive()
code in the code posted in the answer does not. -
Phil Nicholas over 8 yearsWell done. You saved me those same hours of searching. I'm pulling content from SharePoint view REST API, which itself contains Angular markup such as ng-repeat. Your directive made it all work. Thanks!
-
Lakatos Gyula over 8 yearsBe careful, if you pass something like this: "$scope.loadContent = function() { return $sce.trustAsHtml(require('html/main-content.html')); };" to it you can get infinite digest loop. Without the trustAsHtml it works.
-
Jason over 7 yearsThanks for your directive it fixed the problems I was having. Now the angular code gets compiled but too many times. A ng-repeat with 3 object turns into the same values just 3x each. Whats going wrong here?
-
Burak Tokak over 7 yearsIf you have been using
$sce.trustAsHtml
from another function to create the HTML that will be "compiled" with this directive, you should remove it. Thanks to @apoplexy -
Gabriel Andrei almost 7 yearsthis one worked for me. the chosen answer would trigger "Error: $rootScope:infdig Infinite $digest Loop"
-
Dan King over 6 yearsYou shouldn't need the explict
$eval
- you can just useattrs.compile
directly in place of the watched anonymous function. If you just provide a string expression, angular will call$eval
on it anyway. -
Manoj Lasantha about 2 yearsI tried this option. Works like a charm! I also use $sce.trustAsHtml before passing it to the directive. Thanks!