2

In the application I am working on, a service supplies the app with a json, containing HTML. I output the HTML in a template like this:

<div ng-bind-html="renderHtml(widget.article.body)"></div>

This HTML may contain custom directives, like an inline-chart:

directiveModule.directive('inlineChart', function(){
    return {
        restrict: 'A',
        scope: { inline-widget:'=elem' },
        template: '<p ng-bind="inline-widget.blah"></p>'
    };
});

If I use the directive normally in a template, everything works fine, but it seems it is not being touched when used in ng-bing-html with the renderHtml function.

Any suggestions how this could be realised are very much appreciated!

kumarharsh
  • 18,961
  • 8
  • 72
  • 100
mwiegboldt
  • 2,682
  • 2
  • 16
  • 22
  • Have you included [`ngSanitize`](https://docs.angularjs.org/api/ngSanitize) module? Where is renderHTml function? – PSL Oct 27 '14 at 18:18

3 Answers3

6

See https://stackoverflow.com/a/31880192/1345244

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 :)

Community
  • 1
  • 1
Joël
  • 1,563
  • 1
  • 18
  • 31
  • Works like a charm! Thanks! – Asimov4 Oct 04 '15 at 19:01
  • Beware that you may compile several ($watch) different html with the same scope. You should create new child scopes and destroy the previous one every time the $watch value changes. – Mat Oct 21 '16 at 07:59
2

You need to import the ngSanitize module and use the $sce service. It should look something like this:

// Remember the following comes from angular-sanitize.js found on the angular website and
// also must be included in the web app.
angular.module('myApp', ['ngSanitize']);
angular.module('myApp')
  .controller('MyCtrl', function($scope, $sce) {
    //... other controller code
    $scope.renderHtml = function(html) {
      return $sce.trustAsHtml(html);
    };
  });

In short, the $sce service will mark the html as trusted. You can find documentation here

EDIT: I realize I may have not answered the question. It seems that you're asking about binding scope variables to the directive that is rendered within your directive? In order to get elements to compile properly, you're going to have to use the $compile service and change your logic up a bit. First, the template:

<p class="place-directive-here"></p>

Then the directive:

angular.module('myApp')
  .directive('myDirective', function($compile) {
    return {
      scope: {
        inlineWidget: '='
      },
      template: '<p class="place-directive-here"></p>',
      link: function(scope, elem, attrs) {
        var placement = elem.find('.place-directive-here');
        scope.$watch('inlineWidget', function(widget){
          if(!widget){
            return;
          }
          // Compile the html against the scope. This will bind the dom to your
          // current scope
          var newElement = $compile(widget.blah)(scope);
          // Place in the view
          placement.html(newElement);
        });
      }
    };
  });

You can find the compile documentation here. Hopefully this is a more comprehensive answer to what you're looking for.

EDIT 2: For clarification, the directive should look like this on the page:

<div my-directive inline-widget="someInlineWidget"></div>

The place-directive-here class is just a handle for the directive to find your <p> tag and render inside of it. However, if angular is not concerned with the html that is rendered inside of my-directive, the first solution that I provided should work just fine and the template property of my-directive should be:

template: '<p ng-bind-html="renderHtml(inlineWidget.blah)"></p>'
Mike Quinlan
  • 2,873
  • 17
  • 25
  • i don't quite understand what you mean with "place-directive-here". if the directive is called 'myDirective' as in your example, should it be just "my-directive"? – mwiegboldt Oct 28 '14 at 11:20
  • the problem seems to be, that angular is not looking for custom directives inside the rendered html. the 'link' part of the directive you wrote is never activated :( – mwiegboldt Oct 28 '14 at 11:47
  • Sorry. The first solution should work then. I added another edit for clarification. – Mike Quinlan Oct 28 '14 at 16:03
  • Thank you so much for taking the time. Unfortunately this is not the issue I am having. For clarification: The JSON-Feed contains html that is rendered with ng-bind-html="renderHtml(...)". And inside THAT html is a custom directive. But the custom directive is not found, probably because angular is looking for directives before it is rendering the html from the JSON. Which means that the $watch is not working, since the directive doesn't get instanced. – mwiegboldt Oct 28 '14 at 17:10
  • That's because the html that `ng-bind-html` produces does not bind to scope variables (and is not parsed as an angular DOM element) which is why it's not picking up your directive. If you want to do that, you're going to have to use `$compile` as I stated in my first edit. – Mike Quinlan Oct 29 '14 at 18:08
0

An example of Angular directive having attributes with HTML tags

View working example over Plunker

This example shows how to create md-card within directive by passing title and content and content having anchor tag <a>

Use $sce.trustAsHtml within link as $sce works when it is bound to scope

HTML

<div ng-app="myApp">
    <my-app-card title="Card Title" content="Card Content with anchor <a href='http://www.google.com' target='_blank'> Google </a>"></my-app-card>
</div>

JS

var app = angular.module("myApp",['ngMaterial'])
    .directive ('myAppCard', ['$sce', function($sce){
        return {
            scope:{ title:"@title", content:"@content"},
            link : function(scope, element, attr) {
                var hcont = $sce.trustAsHtml(attr.content);
                var html = "<md-card><md-card-content><h2>"+ attr.title + "</h2><p>"+ hcont +"</p></md-card-content></md-card>";
                scope.$watch('myAppCard', function() {
                      element.append(html);
                    }
                )
            }
        }
}]);
Chandre Gowda
  • 870
  • 1
  • 7
  • 24