811

Is it possible to create an HTML fragment in an AngularJS controller and have this HTML shown in the view?

This comes from a requirement to turn an inconsistent JSON blob into a nested list of id: value pairs. Therefore the HTML is created in the controller and I am now looking to display it.

I have created a model property, but cannot render this in the view without it just printing the HTML.


Update

It appears that the problem arises from angular rendering the created HTML as a string within quotes. Will attempt to find a way around this.

Example controller :

var SomeController = function () {

    this.customHtml = '<ul><li>render me please</li></ul>';
}

Example view :

<div ng:bind="customHtml"></div>

Gives :

<div>
    "<ul><li>render me please</li></ul>"
</div>
Arsen Khachaturyan
  • 6,472
  • 4
  • 32
  • 36
Swaff
  • 13,038
  • 4
  • 22
  • 26
  • 1
    Also please see [this question](http://stackoverflow.com/questions/13711735/angular-js-insert-html-with-scripts-that-should-run), asking if it's possible to get scripts in inserted HTML to run. – enigment Dec 04 '12 at 22:19
  • Is it possible to have multiple objects bound to the same ng-bind? like ``` ng-bind="site.address_1 site.address_2 site.zip" – fauverism Mar 26 '15 at 17:28
  • if you have a lot of stuff on your page you will have to alter line 15046 of angular.js (insanity) of `function htmlSanitizer(html) {...`. Angular dev's decided that you should be able to find any html binding by slowly going through all of your pages hidden elements one by one to find that ONE SINGLE missing piece of html. !!! very angry at such an assumption !!! – user3338098 May 21 '15 at 17:35
  • Sorry, the chosen answer by Luke may not be the totally right answer. The right answer can be found in another question [here](http://stackoverflow.com/a/18157958/2197555). Basically, "ng-bind-html-unsafe only renders the content as HTML. It doesn't bind Angular scope to the resulted DOM. You have to use $compile service for that purpose." – gm2008 Dec 10 '15 at 12:41
  • ng-bind removes all internal html. which is not how filter would work, it's ok when filter is the only value – Muhammad Umer Jul 06 '16 at 18:37
  • As of Angular 4, this is the way that now works:
    Taken from this question: https://stackoverflow.com/questions/31548311/angular-html-binding/34424375#34424375
    – Garth Jun 09 '17 at 12:47

17 Answers17

1134

For Angular 1.x, use ng-bind-html in the HTML:

<div ng-bind-html="thisCanBeusedInsideNgBindHtml"></div>

At this point you would get a attempting to use an unsafe value in a safe context error so you need to either use ngSanitize or $sce to resolve that.

$sce

Use $sce.trustAsHtml() in the controller to convert the html string.

 $scope.thisCanBeusedInsideNgBindHtml = $sce.trustAsHtml(someHtmlVar);

ngSanitize

There are 2 steps:

  1. include the angular-sanitize.min.js resource, i.e.:

    <script src="lib/angular/angular-sanitize.min.js"></script>
    
  2. In a js file (controller or usually app.js), include ngSanitize, i.e.:

    angular.module('myApp', ['myApp.filters', 'myApp.services', 
        'myApp.directives', 'ngSanitize'])
    
georgeawg
  • 46,994
  • 13
  • 63
  • 85
Luke Madera
  • 11,437
  • 1
  • 12
  • 9
  • 31
    In Angular 1.2, `ng-bind-html-unsafe` was removed and the two directives were combined. See: https://github.com/angular/angular.js/blob/master/CHANGELOG.md#120-rc1-spooky-giraffe-2013-08-13 – Sasha Chedygov Aug 28 '13 at 07:02
  • 60
    Without using ngSanitize, it can be done now by using `$sce`. Inject it into the controller and pass the html through it. `$scope.thisCanBeusedInsideNgBindHtml = $sce.trustAsHtml(someHtmlVar);` Otherwise I kept getting `attempting to use an unsafe value in a safe context` – Matsemann Oct 09 '13 at 10:37
  • 3
    We need a bit of a cleanup here which is the right way nothing seems to be working for me. – landed Mar 15 '14 at 11:15
  • 9
    http://stackoverflow.com/questions/21829275/angular-js-shows-html-within-the-tag – anpatel Mar 25 '14 at 16:23
  • 1
    What if html needs to be inserted into html tag attribute? – Eugene Apr 01 '14 at 16:13
  • 7
    Just so people aren't disheartened, the latest update of this answer, coupled with the ngSanitize requirement at the bottom of the answer, does in fact work. – Ben Cull Jul 11 '14 at 01:37
  • 2
    1) ng-bind-html and then 2) sce.trustAsHtml() .... Is there a way to do it in one step? – Cherven May 21 '15 at 00:30
  • 1
    Thank you very much. This post saved me a lot of time. Just a note to not forget to include the dependencies, as shown here: https://docs.angularjs.org/api/ngSanitize/service/$sanitize – VSO Jul 15 '15 at 19:29
  • Soi what are the required steps to acheiveing the goal? – Dezmen Ceo Sykes Aug 31 '15 at 17:32
  • used the $sce method and it worked great for me. don't forget to add ng-bind-html="thisCanBeusedInsideNgBindHtml" to your element to generate your content. – Eolis Jan 06 '16 at 00:50
  • 1
    Hello XSS :) be sure the content is trusted, not user input for example – mpgn Jan 19 '16 at 20:19
  • but it does override all existing html so not as useful as filter – Muhammad Umer Jul 06 '16 at 18:30
  • my $scope is not working inside html {{test}} any solution?? – Parshuram Kalvikatte Nov 24 '16 at 14:11
  • this works:
    – Juan May 07 '17 at 02:41
  • ngSanitize option worked perfectly for me with Angularjs 1.6 version – Rennish Joseph Jun 15 '18 at 16:49
  • Working Answer - https://stackoverflow.com/questions/21829275/angular-js-shows-html-within-the-tag – Surya R Praveen Jan 07 '19 at 11:35
312

You can also create a filter like so:

var app = angular.module("demoApp", ['ngResource']);

app.filter("trust", ['$sce', function($sce) {
  return function(htmlCode){
    return $sce.trustAsHtml(htmlCode);
  }
}]);

Then in the view

<div ng-bind-html="trusted_html_variable | trust"></div>

Note: This filter trusts any and all html passed to it, and could present an XSS vulnerability if variables with user input are passed to it.

SethWhite
  • 1,613
  • 14
  • 23
Katie Astrauskas
  • 3,139
  • 1
  • 8
  • 3
  • @Katie Astrauskas, thank you for the anwer! Very clean way. BTW `ngResource` dependency is not neccesary. – Athlan Mar 07 '15 at 12:09
  • 28
    Only use this when you completely trust the HTML. This does not sanitize the HTML in any way, but only allows Angular to inject it into the page. Malicious HTML can provoke XSS attacks. – jan.vdbergh Apr 23 '15 at 20:38
  • If performance is important, you should avoid using filters. A filter will trigger two digests each time. – Tantelope Aug 09 '15 at 06:00
  • 14
    Why is the filter called `sanitize`? This is so misleading as it doesn't actually sanitize anything. Instead it should be called `trust`, `trustSafe` or something similar. – Denis Pshenov Oct 09 '15 at 19:52
  • 1
    Wonderful answer. `rawHtml` is my name for the filter instead of `sanitize`. – Wumms Dec 02 '15 at 18:21
  • @Katie indeed, ['ngResource'] dependency not needed for this, its not a fully sanitation. – misterzik Jan 02 '16 at 03:25
  • can this be used like: {{ whatever_needs_to_be_sanitized | sanitize }} instead of bound to the element? – tonejac Jun 24 '16 at 04:40
122

Angular JS shows HTML within the tag

The solution provided in the above link worked for me, none of the options on this thread did. For anyone looking for the same thing with AngularJS version 1.2.9

Here's a copy:

Ok I found solution for this:

JS:

$scope.renderHtml = function(html_code)
{
    return $sce.trustAsHtml(html_code);
};

HTML:

<p ng-bind-html="renderHtml(value.button)"></p>

EDIT:

Here's the set up:

JS file:

angular.module('MyModule').controller('MyController', ['$scope', '$http', '$sce',
    function ($scope, $http, $sce) {
        $scope.renderHtml = function (htmlCode) {
            return $sce.trustAsHtml(htmlCode);
        };

        $scope.body = '<div style="width:200px; height:200px; border:1px solid blue;"></div>'; 

    }]);

HTML file:

<div ng-controller="MyController">
    <div ng-bind-html="renderHtml(body)"></div>
</div>
Community
  • 1
  • 1
anpatel
  • 1,924
  • 4
  • 19
  • 35
66

Fortunately, you don't need any fancy filters or unsafe methods to avoid that error message. This is the complete implementation to properly output HTML markup in a view in the intended and safe way.

The sanitize module must be included after Angular:

<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-sanitize.js"></script>

Then, the module must be loaded:

angular.module('app', [
  'ngSanitize'
]);

This will allow you to include markup in a string from a controller, directive, etc:

scope.message = "<strong>42</strong> is the <em>answer</em>.";

Finally, in a template, it must be output like so:

<p ng-bind-html="message"></p>

Which will produce the expected output: 42 is the answer.

Pier-Luc Gendreau
  • 11,989
  • 4
  • 51
  • 60
  • 1
    Try some html like `
    `... If it surprises u then update your answer plz.. Because I tested all 10+vote solutions.. Your solution did'nt work for me to see my input tags.. fine otherwise.. I had use `$sce.trustAsHtml(html)`
    – Sami Jul 31 '15 at 12:59
  • This solution works, care to post a jsfiddle or plunkr? – Pier-Luc Gendreau Jul 31 '15 at 14:55
  • This should really be the answer if using latest angular – Sandeep Nov 09 '15 at 13:24
62

I have tried today, the only way I found was this

<div ng-bind-html-unsafe="expression"></div>

Damax
  • 745
  • 5
  • 2
  • 6
    This solution should be used only if the source is trustful to avoid cross-site scripting attacks. – Bertrand Oct 04 '12 at 18:39
  • 3
    As of Angular 1.0.2, this works for me, with no other files or hookup required. – enigment Dec 04 '12 at 20:41
  • 1
    Using Angular 1.0.8 and this worked for me. Heed @Bertrand's warning though, be sure you trust the source. – Jack Slingerland Nov 07 '13 at 13:18
  • 12
    For future reference ng-bind-html-unsafe was removed in the version 1.2. Now you need the ngSanitize module and to bind unsafe html you should use the $sce.trustAsHtml method. – Lucas Lazaro Dec 12 '13 at 17:19
53

ng-bind-html-unsafe no longer works.

This is the shortest way:

Create a filter:

myApp.filter('unsafe', function($sce) { return $sce.trustAsHtml; });

And in your view:

<div ng-bind-html="customHtml | unsafe"></div>

P.S. This method doesn't require you to include the ngSanitize module.

Bidhan Bhattarai
  • 940
  • 2
  • 11
  • 19
  • 3
    This is the best solution I've seen here for Angular 1.2. The solution using `$sce` in the accepted answer didn't work for me and I didn't want to include a additional dependency for something so trivial. – Daniel Bonnell Mar 10 '16 at 01:59
  • The solution by Bidhan Bhattarai worked for me. Angular 1.6.1 – mediaguru Apr 16 '17 at 03:13
26

on html

<div ng-controller="myAppController as myCtrl">

<div ng-bind-html-unsafe="myCtrl.comment.msg"></div>

OR

<div ng-bind-html="myCtrl.comment.msg"></div

on controller

mySceApp.controller("myAppController", function myAppController( $sce) {

this.myCtrl.comment.msg = $sce.trustAsHtml(html);

works also with $scope.comment.msg = $sce.trustAsHtml(html);

Sirko
  • 65,767
  • 19
  • 135
  • 167
Sotos
  • 406
  • 4
  • 4
  • 1
    `$sce` is neat but couldn't a user just add a breakpoint here and restore any malicious code to `this.myCtrl.comment.msg` using a debugger? – BradGreens Apr 08 '14 at 20:01
  • then again BradGreens, would you be able to do the same with ng-bind-html-unsafe as well? – CodeOverRide Apr 28 '14 at 18:07
  • 4
    If someone wants to hack there own browser they can, who cares. It won't effect other users. @BradGreens Is that the question? – Chris Stephens May 14 '14 at 17:53
  • @ChrisStephens you're correct. I guess that does answer my question but my opinion is these features are closer to perceived security than real security. Perhaps it protects against some kind of automated attacks? I've never clearly grasped why doing this things really helps the app. My app has to add a filter to EVERY instance of rendering wysiwyg HTML because it could have inline CSS, which `ng-bind-html` strips out. – BradGreens May 14 '14 at 21:57
  • 1
    Well these features help with reducing secure coding mistakes. In particular the issue with markup/code injection. By default all bound data is encoded for display. So basically if you want to output markup this forces you to think about what you are trying to do. Without these features you can do a lot with server side security alone but to separate concerns the client app should charge of handling the data correctly for display. – Chris Stephens May 15 '14 at 16:01
  • appreciate the insight – BradGreens May 16 '14 at 15:51
9

I found that using ng-sanitize did not allow me to add ng-click in the html.

To solve this I added a directive. Like this:

app.directive('htmldiv', function($compile, $parse) {
return {
  restrict: 'E',
  link: function(scope, element, attr) {
    scope.$watch(attr.content, function() {
      element.html($parse(attr.content)(scope));
      $compile(element.contents())(scope);
    }, true);
  }
}
});

And this is the HTML:

<htmldiv content="theContent"></htmldiv>

Good luck.

Matt
  • 26,570
  • 19
  • 63
  • 74
6

Just did this using ngBindHtml by following angular(v1.4) docs,

<div ng-bind-html="expression"></div> 
and expression can be "<ul><li>render me please</li></ul>"

Make sure you include ngSanitize in the module's dependencies. Then it should work fine.

Henry Neo
  • 2,317
  • 1
  • 21
  • 27
4

Another solution, very similar to blrbr's except using a scoped attribute is:

angular.module('app')
.directive('renderHtml', ['$compile', function ($compile) {
    return {
      restrict: 'E',
      scope: {
        html: '='
      },
      link: function postLink(scope, element, attrs) {

          function appendHtml() {
              if(scope.html) {
                  var newElement = angular.element(scope.html);
                  $compile(newElement)(scope);
                  element.append(newElement);
              }
          }

          scope.$watch(function() { return scope.html }, appendHtml);
      }
    };
  }]);

And then

<render-html html="htmlAsString"></render-html>

Note you may replace element.append() with element.replaceWith()

jmbmage
  • 2,115
  • 1
  • 20
  • 40
3

you can also use ng-include.

<div class="col-sm-9 TabContent_container" ng-include="template/custom.html">
</div>

you can use "ng-show" to show hide this template data.

Giacomo1968
  • 23,903
  • 10
  • 59
  • 92
3

there is one more solution for this problem using creating new attribute or directives in angular.

product-specs.html

 <h4>Specs</h4>
        <ul class="list-unstyled">
          <li>
            <strong>Shine</strong>
            : {{product.shine}}</li>
          <li>
            <strong>Faces</strong>
            : {{product.faces}}</li>
          <li>
            <strong>Rarity</strong>
            : {{product.rarity}}</li>
          <li>
            <strong>Color</strong>
            : {{product.color}}</li>
        </ul>

app.js

 (function() {
var app = angular.module('gemStore', []);    
app.directive("     <div ng-show="tab.isSet(2)" product-specs>", function() {
return {
  restrict: 'E',
  templateUrl: "product-specs.html"
};
});

index.html

 <div>
 <product-specs>  </product-specs>//it will load product-specs.html file here.
 </div>

or

<div  product-specs>//it will add product-specs.html file 

or

<div ng-include="product-description.html"></div>

https://docs.angularjs.org/guide/directive

yugi
  • 732
  • 13
  • 25
2

here is the solution make a filter like this

.filter('trusted',
   function($sce) {
     return function(ss) {
       return $sce.trustAsHtml(ss)
     };
   }
)

and apply this as a filter to the ng-bind-html like

<div ng-bind-html="code | trusted">

and thank to Ruben Decrop

bahri noredine
  • 471
  • 5
  • 11
1

Use

<div ng-bind-html="customHtml"></div>

and

angular.module('MyApp', ['ngSanitize']);

For that, you need to include angular-sanitize.js, for example in your html-file with

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-sanitize.js"></script>
Patricia Heimfarth
  • 2,688
  • 2
  • 22
  • 29
0

Here's a simple (and unsafe) bind-as-html directive, without the need for ngSanitize:

myModule.directive('bindAsHtml', function () {
    return {
        link: function (scope, element, attributes) {
            element.html(scope.$eval(attributes.bindAsHtml));
        }
    };
});

Note that this will open up for security issues, if binding untrusted content.

Use like so:

<div bind-as-html="someHtmlInScope"></div>
-1

Working example with pipe to display html in template with Angular 4.

1.Crated Pipe escape-html.pipe.ts

`

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({name : 'keepHtml', pure : false})
export class EscapeHtmlPipe implements PipeTransform{
 constructor(private sanitizer : DomSanitizer){
 }
 transform(content){
  return this.sanitizer.bypassSecurityTrustHtml(content);
 }
}

` 2. Register pipe to app.module.ts

 import {EscapeHtmlPipe} from './components/pipes/escape-html.pipe';
    declarations: [...,EscapeHtmlPipe]
  1. Use in your template

        <div class="demoPipe"  [innerHtml]="getDivHtml(obj.header) | keepHtml">
  2. getDivHtml() { //can return html as per requirement}

    Please add appropriate implementation for getDivHtml in associated component.ts file.

Sagar Misal
  • 135
  • 1
  • 5
-2

Just simple use [innerHTML], like below:

<div [innerHTML]="htmlString"></div>

Before you needed to use ng-bind-html...

Alireza
  • 83,698
  • 19
  • 241
  • 152
  • 1
    This is only available within Angular (Angular version 2+), not AngularJS (Angular version 1). – ste2425 Jan 29 '20 at 08:38