Picking up jQuery dom manupilations with AngularJS

AngularJS and jQuery are two of many frameworks who manipulate the dom.
They both have their own way of doing this.
I prefer AngularJS for its data driven approach.
Mixing jQuery dom manipulation and AngularJS data binding is not a success, it is a bad practice and should be avoided.
But sometimes it just cannot be avoided and you just have to deal with it. In a project I was working on, jQuery was used for dom manipulation. My task was to build a new feature with AngularJS which uses the same part of the dom. Completely refactoring was not an option so I had to deal with dom changes triggered by jQuery. AngularJS wont pick up expressions when they are injected in the dom. I figured out the following solution.

Monitoring (a section of) the dom

In my first attempt, I tried to monitor a section of the dom. Every change will be picked up and compiled.

angular.module('myApp', [] ,function ($compileProvider) {
    $compileProvider.directive('compile', function ($compile) {
            return function (scope, element, attrs) {
                scope.$watch(
                  function (scope) {
                      return element.html();
                  },
                  function (value) {
                      $compile(element.contents())(scope);
                  }
                );
            };
        });
    });

This directive is triggered by adding the compile attribute to an element in the app scope.

<div ng-app="myApp">
    <div compile>
    ....
    </div>
</div>

At first, I thought it worked perfectly.
It detected every change. But when I was adding more angular expressions, I noticed a performance drop.
Every data bind was causing a trigger to compile the section being watched. This was causing the performance drop. Though it wasn’t an issue yet, I just did not think it is a good solution.

Monitoring an attribute of an element in the dom

In my second attempt, I tried to limit the compiles to a minimum.
I was asking myself what I could use as a trigger for an update. I realized that the correct answer is “it depends”. It depends seems to be the answer to every question in developer land. I tried to keep the trigger as flexible as possible and came up with this solution.

angular.module('myApp', [] ,function ($compileProvider) {
    $compileProvider.directive('compileWhenAttributeOfChildChanges', function ($compile) {
            return function (scope, element, attrs) {
                scope.$watch(
                  function (scope) {
                      var attributeToWatch = element.attr("compile-when-attribute-of-child-changes");
                      return element.children().attr(attributeToWatch);
                  },
                  function (value) {
                      $compile(element.contents())(scope);
                  }
                );
            };
        });
    });

This directive is triggered by elements with attribute compile-when-attribute-of-child-changes. It uses the value of this attribute as attribute to watch on its child element. When this attribute changes value, a compile is triggered.

<div ng-app="myApp" compile-when-attribute-of-child-changes="watch">
    <div watch="valueBeingWatched">
    ....
    </div>
</div>

In this example, a compile is triggered when the “watch” attribute changes.
this solution worked well and there are no unnecessary compiles.

The following two tabs change content below.
I'm a software developer from Utrecht. Interested in DDD, continuous delivery, new technologies & frameworks.

Latest posts by Vincent Keizer (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *