angular ng-bind-html and directive within it

AngularjsAngularjs Directive

Angularjs Problem Overview


Plunker Link

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?

Angularjs Solutions


Solution 1 - Angularjs

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 - Angularjs

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 - Angularjs

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 - Angularjs

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 - Angularjs

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.

Solution 6 - Angularjs

Best solution what I've found! I copied it and it work's exactly as I needed. Thanks, thanks, thanks ...

in directive link function I have

app.directive('element',function($compile){
  .
  .
     var addXml = function(){
     var el = $compile('<xml-definitions definitions="definitions" />')($scope);
     $scope.renderingElement = el.html();
     }
  .
  .

and in directive template:

<span compile="renderingElement"></span>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAmitavaView Question on Stackoverflow
Solution 1 - AngularjsvkammererView Answer on Stackoverflow
Solution 2 - Angularjsuser3075469View Answer on Stackoverflow
Solution 3 - AngularjsJoëlView Answer on Stackoverflow
Solution 4 - AngularjsapoplexyView Answer on Stackoverflow
Solution 5 - AngularjsMStruttView Answer on Stackoverflow
Solution 6 - AngularjsyustmeView Answer on Stackoverflow