OdeToCode IC Logo

Using $compile in Angular

Wednesday, May 7, 2014

Creating a custom directive in AngularJS is easy, let’s start with the HTML for a simple example.

{{ message }}
<div otc-dynamic></div>

The above markup is using a directive named otcDynamic, which only provides a template.

app.directive("otcDynamic", function(){
   return {
       template:"<button ng-click='doSomething()'>{{label}}</div>"
   };
});

When combined with a controller, the presentation will allow the user to click a button to see a message appear on the screen.

app.controller("mainController", function($scope){

    $scope.label = "Please click";
    $scope.doSomething = function(){
      $scope.message = "Clicked!";
    };

});

Make It Dynamic

Next, imagine the otcDynamic directive can’t use a static template. The directive needs to look at some boolean flags, user data, or service information, and dynamically construct the template markup. In the following example, we’ll only simulate this scenario. We are still using a static string, but we’ll pretend we created the string dynamically and use element.html to place the markup into the DOM.

app.directive("otcDynamic", function(){
    return {
        link: function(scope, element){
            element.html("<button ng-click='doSomething()'>{{label}}</button>");
        }
    };
});

The above sample no longer functions correctly and will only render a button displaying the literal text {{label}} to a user.

Markup has to go through a compilation phase for Angular to find and activate directives like ng-click and {{label}}.

Compilation

The $compile service is the service to use for compilation. Invoking $compile against markup will produce a function you can use to bind the markup against a particular scope (what Angular calls a linking function). After linking, you’ll have DOM elements you can place into the browser.

app.directive("otcDynamic", function($compile){
    return{
        link: function(scope, element){
            var template = "<button ng-click='doSomething()'>{{label}}</button>";
            var linkFn = $compile(template);
            var content = linkFn(scope);
            element.append(content);
        }
    }
});

If you have to $compile in response to an element event, like a click event or other non-Angular code, you’ll need to invoke $apply for the proper scope lifecycle.

app.directive("otcDynamic", function($compile) {
    
    var template = "<button ng-click='doSomething()'>{{label}}</button>";
    
    return{
        link: function(scope, element){
            element.on("click", function() {
                scope.$apply(function() {
                    var content = $compile(template)(scope);
                    element.append(content);
               })
            });
        }
    }
});