public

Using $compile to compile HTML strings in Angular

Say you want to bind a string to an element in Angular. No real problem, you just use expressions or ngBind. If the string contains HTML that you want to

9 years ago

Latest Post Deferring decisions in Evolutionary Architecture by Tim Sommer public

Say you want to bind a string to an element in Angular. No real problem, you just use expressions or ngBind.
If the string contains HTML that you want to parse, you can use ngSanitize.
But what if your HTML string contains a button that has an ng-click directive included in it? This won't work automatically, you'll need to use $compile.

So what is this compile service? According to the Angular docs:

Compiles an HTML string or DOM into a template and produces a template function, which can then be used to link scope and the template together.

So lets say that we have the following string:

$scope.htmlString = '<div><input type="button" ng-click="clickMe()" value="click me!"/> </div>';

Which you want to show like so:

<div><p>{{htmlString}}</p></div>

This will output the exact string in the HTML. If we use ngSanitize, it will render the HTML, but it won't bind the click event. So instead of using ngSanitize, we'll use $compile.

Let's create a simple directive that compiles the string above.

(function(){
"use strict";
angular.module("CompileDirective", [])
  .directive('dynamicElement', ['$compile', function ($compile) {
      return { 
        restrict: 'E', 
        scope: {
            message: "="
        },
        replace: true,
        link: function(scope, element, attrs) {
            var template = $compile(scope.message)(scope);
            element.replaceWith(template);               
        },
        controller: ['$scope', function($scope) {
           $scope.clickMe = function(){
                alert("hi")
           };
        }]
      }
  }])
  .controller("DemoController", ["$scope", function($scope){
      $scope.htmlString = '<div><input type="button" ng-click="clickMe()" value="click me!"/> </div>';
  }])

}());

With the following HTML:

<div ng-controller="DemoController">
  <dynamic-element message='htmlString'></dynamic-element>
</div>

We're creating a 'dynamicElement' directive that has a 'message' object in its isolated scope. That object is compiled and stored in the 'template' variable.
This object contains a jQlite DOM object that we can use to replace the entire directive. Since it is an object, we can't just bind the 'template' object as a string. This is why we should always use a directive in combination with $compile.
In the controller function of the directive we specify the 'clickMe' function, which will be invoked when the compiled button is clicked.

This is a simple way of creating a directive that compiles any html string with its own isolated scope. Since there are many flavours, alternatives and approaches available for this problem, please don't hesitate to comment to this post if you have questions or remarks.

Tim Sommer

Published 9 years ago

Comments?

Leave us your opinion.