AngularJS and $compile

A Quick Introduction to Angular

AngularJS is a newer MVC Javascript framework created by developers at Google.  It provides seamless linking between models and views, resources for doing CRUD functions against a REST server with minimal code, a powerful way of encapsulating functionality in elements called Directives and much more. With the large number of Javascript frameworks available these days, Angular is particularly appealing to me because it pushes developers to write encapsulated, well-architected javascript applications and really encourages testing.

This blog post assumes that you have a fair bit of knowledge about how Angular works. If you want to get more introductory information, you can find quite a bit of in the AngularJS documentation and there are many other blogs and tutorials available. I found the videos at egghead.io particularly helpful.

Partials

Like most MVC frameworks, Angular provides a templating framework that allows the use of partials. Partials are templates which represent only a portion of a page, generally the view  for a small piece of resuable functionality. In Angular, this small piece of reusable functionality is generally a Directive. A directive could represent, for example, an image uploader. Its partial might look like:

And it may have quite a bit of code in its linking function (we’ll talk more about this later) / controller it to allow the image to be uploaded without refreshing the page, show progress, allow for removing the file, etc.

To add this functionality into another partial – e.g. the form for one of the elements of your application, all you would have to do is insert the bit of markup representing this directive ( <div image-uploader inputId="my-models-image" placeholder="Image Path" />) into the other partial.

Adding the Functionality

Now that we have the markup, how does the functionality get attached? This happens through a process which AngularJS calls ‘compiling’. Compiling is a step in the process of rendering the markup which attaches functionality to the DOM elements. Its steps include replacing / filling the directive element with its template, creating a scope (an area for local variables / functionality specific to each directive – note that there are several ways that scopes can be declared), attaching events to various elements within the directive and setting up watches to check for changes inside of the scope and do something when they occur.

The compile function runs in 2 general steps which are called Compilation and Linking. Compilation allows manipulation of the template and creation of the linking function. The linking function allows for the attaching of events and handling of scope. When you create a new directive, you have the opportunity to write compile and/or linking functions for it to attach your custom behavior.

When the markup is loaded, through an ng-view (triggered by a route change generally) or a page load, Angular goes through and compiles the new markup. Partials (templates) are loaded by ajax (or can be embedded in script tags) and rendered on the page.

Special Cases

Sometimes an application has additional needs beyond just displaying a page per route. For example, we might have a drag and drop interface which allows users to insert new instances of a variety of directives into the page. To support this, when a new directive is placed by the user, we need to trigger the compilation of that directive so that it will get the proper events and functionalities attached. (As an aside, if you’re inserting new instances of only one directive, you should use an ng-repeat to handle this functionality for you).

To compile a DOM element / bit of markup, we will need to use the $compile service.  $compile takes markup or a jQuery object, and returns a linking function which then must be called with a scope for it to bind to.  Generally, this looks like:  scope.subelement = $compile(newDirective)(scope); . Once the linking function is called, it returns an angular.element which has the functionality of a jqLite / jQuery object, but also has a scope and various other angular specific data.  Before or after compilation, the element can be inserted into the page.

Generally, you want to insert the new element so its a child of the element which creates the parent scope.  So if i have some directive A, and in its linking function, I create and compile directive B – I should insert B’s element inside of A’s element so that the DOM tree and scope hierarchy match.

And that’s it, now you can dynamically create and insert directives into your application.  This should be used with care, and other options, like ng-repeat, should be preferred if possible. That said, sometimes these options just don’t provide the required functionality. For these cases, angular provides a nice way to implement custom functionality.

  • Josh

    I wrote a quick example to demonstrate how you can do this.

    http://jsfiddle.net/jec006/sSw2M/

    • Michael

      Thanks for the example Josh! it works Great. I was wondering if whether the directives are defined in different modules might have an impact on the success of your solution. (It’s not working for me).

      • Josh

        Hi Micheal,

        You can definitely declare the directives in different modules, you just need to make sure that the original app / controller etc includes that module. Something like:

        angular.module(‘myModule’, ['mySecondModule']);

        Hope that helps.

  • Simon

    Hi Josh, thanks for your article! I am using a compile-function but discover problems when I want to remove the compiled element again. The DOM and the connected data is not cleaned up correctly what leads to a memory leak. How do I remove elements that I compiled and linked using $compile?