How to make ng-bind-html compile angularjs code

JavascriptHtmlAngularjsWeb

Javascript Problem Overview


I am working with angularjs 1.2.0-rc.3. I'd like to include html code into a template dynamically. For that I use in the controller :

html = "<div>hello</div>";
$scope.unicTabContent = $sce.trustAsHtml(html);

In the template I got :

<div id="unicTab" ng-bind-html="unicTabContent"></div>

It works fine for regular html code. But when I try to put angular template it is not interpreted, it is just included in the page. For example I'd like to include :

<div ng-controller="formCtrl">
    <div ng-repeat="item in content" ng-init="init()">
    </div>
</div>

Thanks a lot

Javascript Solutions


Solution 1 - Javascript

This solution doesn't use hardcoded templates, and you can compile Angular expressions embedded within an API response.


Step 1. Install this directive: https://github.com/incuna/angular-bind-html-compile

Step 2. Include the directive in the module.

angular.module("app", ["angular-bind-html-compile"])

Step 3. Use the directive in the template:

<div bind-html-compile="letterTemplate.content"></div>

Result:

Controller Object

 $scope.letter = { user: { name: "John"}}

JSON Response

{ "letterTemplate":[
    { content: "<span>Dear {{letter.user.name}},</span>" }
]}

HTML Output =

<div bind-html-compile="letterTemplate.content"> 
   <span>Dear John,</span>
</div>

For reference sake, here's the relevant directive:

(function () {
    'use strict';

    var module = angular.module('angular-bind-html-compile', []);

    module.directive('bindHtmlCompile', ['$compile', function ($compile) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                scope.$watch(function () {
                    return scope.$eval(attrs.bindHtmlCompile);
                }, function (value) {
                    element.html(value);
                    $compile(element.contents())(scope);
                });
            }
        };
    }]);
}());

Solution 2 - Javascript

This is what I've made, no idea if it's the angular wayTM, but it works and is super simple;

.directive('dynamic', function($compile) {
    return {
        restrict: 'A',
        replace: true,
        link: function (scope, element, attrs) {
            scope.$watch(attrs.dynamic, function(html) {
                $compile(element.html(html).contents())(scope);
            });
        }
    };
});

So;

<div id="unicTab" dynamic="unicTabContent"></div>

Edit: I found the angular way, and it's super simple.

$templateCache.put('unicTabContent', $sce.trustAsHtml(html));

<div id="unicTab" ng-include="'unicTabContent'"></div>

Don't need to make your own directives or anything. But it's a bind-once sort of deal, it wont see changes made to your html like the custom directive does.

Solution 3 - Javascript

As Vinod Louis says in his comment, the best way to do that was to use templates. I had to define a template outside of the regular code, for example I added that code inside of my index.html :

<script type="text/ng-template" id="unic_tab_template.html">
	<div ng-switch on="page">
		<div ng-switch-when="home"><p>{{home}}</p></div>
		<div ng-switch-when="form">
			<div ng-controller="formCtrl">
			    <div ng-repeat="item in content">{{item.name}}:{{item.value}}</div>
			</div>
		</div>
		<div ng-switch-default>an error accured</div>
	</div>
</script>

This template is conditional, so depending on the value of $scope.page, it switches between the 3 templates (the third being an error handler). To use it I had :

<div id="unicTab" ng-controller="unicTabCtrl">
	<div ng-include="'unic_tab_template.html'"></div>
</div>

That way my page changes depending on the $scope inside of my unicTabCtrl controller.

To conclude the idea of inserting angularsjs template seams to be difficult to realize ($compile seams to be the solution, but I wasn't able to make it work). But instead you may use conditional templating.

Solution 4 - Javascript

I was trying to do the same thing and came across this module.

http://ngmodules.org/modules/ng-html-compile

I just included it and then I was able to use "ng-html-compile" instead of "ng-bind-html"

Solution 5 - Javascript

One way is use a directive for purpose of inserting custom templates that include angular expresssions

<div id="unicTab" unic-tab-content></div>

app.directive("unicTabContent",function(){
   return {
      restrict:"A",
      template:'{{unicTabContent}}'
   }
})

Solution 6 - Javascript

My solution to similar problem in my current app without using template (not elegant, but working):

directive('ngBindHtmlCompile', ['$compile', function ($compile) {
    return {
        restrict: 'A',
        compile: function compile(tElement, tAttributes, transcludeFn) {
            return function postLink(scope, element, attributes) {
                scope.$watch(function() {
                    return scope.$eval(attributes.ngBindHtml);
                }, function(newValue, oldValue) {
                    $compile(element.children())(scope);
                });
            };
        }
    };
}]);

It requires ngBindHtml on the same element and compiles the element content after it changes with ngBindHtml.

<div id="unicTab" ng-bind-html="unicTabContent" ng-bind-html-compile></div>

ng-html-compile looks similar but at first glance it won't be recalculated when the template content is changing. But I haven't tried it.

Solution 7 - Javascript

The code below is much simpler using Angular's built-in $interpolate and $sce objects. First inject the $interpolate and $sce Angular objects into your directive as you do anything custom you need in your directive.

amqApp.directive('myDir', ['$interpolate', '$sce', function ($interpolate,$sce ) {...}

Then create all your scoped variables found in your imported html expressions...

$scope.custom = 'Hello World';

Next use $interpolate to process your custom HTML and its expressions...then make sure you use the $sce object to trust it as HTML before binding...

var html = $interpolate('<b>{{custom}}</b>')($scope);    
$scope.data = $sce.trustAsHtml(html);

Finally, in your view, just make sure use an element with the "ng-bind" or "ng-bind-html" on it in your view display. I found the $sce piece wont display the HTML as HTML (sees it as text) if you don't bind it in your html template like this...

<span ng-bind-html="data"></span>

You should see in bold...

Hello World

I used this trick to import in text/HTML with custom angular {{expressions}} from a web.config.

Solution 8 - Javascript

A lot simplified solution of @clement-roblot based on built-in templates.

Controller:

app.controller('TestCtrl', [
    '$scope',
    '$templateCache',
    function ($scope, $templateCache) {
        $templateCache.put('test.html', '2 + 2 = {{ 2 + 2 }}');
    }
]);

View:

<div ng-include="'test.html'"></div>

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
QuestionClement RoblotView Question on Stackoverflow
Solution 1 - JavascriptRyan.layView Answer on Stackoverflow
Solution 2 - JavascriptHashbrownView Answer on Stackoverflow
Solution 3 - JavascriptClement RoblotView Answer on Stackoverflow
Solution 4 - JavascriptnbarthView Answer on Stackoverflow
Solution 5 - JavascriptcharlietflView Answer on Stackoverflow
Solution 6 - JavascriptLukasz FrankowskiView Answer on Stackoverflow
Solution 7 - JavascriptStokelyView Answer on Stackoverflow
Solution 8 - JavascriptStalinkoView Answer on Stackoverflow