Angular and DOM Manipulation

So it’s been about three months now that I have started my adventures into Angular, and I’m definitely enjoying it. But its also been a bit difficult moving from other frameworks like Backbone where I had full control of the processes. In this article I briefly discuss how Angular lets you focus on the bigger picture using bindings.

If you’re coming from a Backbone background to Angular or even Ember you’ll be surprised with how easy it is to create dynamic applications without having to do all the dynamic DOM manipulation you had to do previously. However, if you’re like me – you might find it frustrating how to achieve this easily – after all Angular doesn’t use the idea of views, instead they’re called directives. Think components and separation of concerns.

But lets start by discussing the scope. Once we get scope down, we’ll move to using directives (what they are, how you use them and what they look like). Finally we’ll go through a simple example that might help you further your understanding of the subject.

Angular Scope

If you’ve worked with Angular recently or are going to soon, then you might have or will see something like this:

$scope is what is used in Angular to render data within your application. There are more extravagant explanations and definitions out there, but for the beginning developer – think of $scope as exposing data to your “views”. So if we look at the above example, we have an array of books that we can iterate and render like so:

The ng-repeat directive receives this object from the inherited scope set in MainCtrl, which we will see in the full example later on. For now, know that scope gets exposed to us through these directives and that we can use the scope to render information or do what we like.

Another thing to note is that $scope is just regular JavaScript, and does typical prototypal inheritance. So lets say we have a DashboardController within MainController, DashboardController’s scope will inherit from MainController’s scope – as you would expect from any JavaScript framework. Directives also inherit from its controllers scope, unless otherwise specified. For more information, read the following article.

Angular Directives

In the old days we would have to do all of the DOM manipulation by hand using getElementById, createElement, setAttribute. Things got better when libraries like jQuery and MooTools were released. These libraries made it easy for us to append and remove elements. Angular removes this step by using bindings, which will detect changes and update the UI. Directives are where the magic between Angular and DOM occurs.

As I mentioned, directives inherit their scope from the controller they exist in. Therefore you can access the scope very easily from within a directive’s definition. As you may recall in my last post, the directive handles all executions in the DOM, or should, and so using data that is available in the scope is extremely helpful.

A simple directive looks like this:

This is a simple directive definition, defining the app-form directive and attaching functionality to that element. Lets look at the markup that implements this directive:

In this example the appForm directive is declared using the app-form directive on the form element. Going back to the scope explanation in the previous section, this directive will also inherit the scope from the controller MainCtrl. This allows us to access all information in the scope and achieve DOM Manipulation within the directive.

A Simple Example

Traditionally we would access the DOM manually, perhaps caching our elements and then attaching click handlers that would then detach / clone an element and then ultimately append it to any section we wanted. To see this in actions lets take a look at an example that utilizes this traditional method in Angular.

Check out this Pen!

In the above example, take a closer look at the method addBooks. In this method we create a jQuery object that stores a list element in memory. Once the HTML is set for that list element we then query the DOM for the book list and append that list element we created.

This method is perfectly fine, but does come with some drawbacks that are quite severe when creating large scale applications. First of all, this method of DOM manipulation is outside of the Angular scope – so if there were any calls to the backend,  you will need to add an additional step of appending that information to the book list array. If you take that route then you are now adding more work to your list, albeit small but still more work.

The alternative way of handling DOM manipulation in Angular is to stick with your scope and to use your JavaScript objects for any kind of manipulation – triggering a $digest when you need it. The $digest gets called by $apply, which is a method on the Angular scope.  Lets take a look at how this can be achieved, using the same example with some differences in the addBooks method.

Check out this Pen!

As you can see in the example above, the change is simple, we just add a book object to the book list array that is attached on the scope. We then trigger a $digest by calling $apply. This will make Angular refresh the UI and do exactly the same thing that we did in our previous example. Cool, huh?!

There are some issues that you do need to keep in mind when using $apply, and these are well covered by Jim Hoskins post AngularJS and scope.$apply. Jim does a great job summarizing scope, $apply and $digest. After reading his article you should have a firm understanding of how $apply and $digest are used correctly in most Angular projects.

Well, I hope you found this article helpful and I look forward to hearing your feedback and questions or concerns. As always hit me up on the comments and Ill make sure to get back to you as soon as I can.

Cheers!

  • sellweek

    The two Codepen examples contain the same code.

    • alvincrespo

      @sellweek:disqus Correct! I’m sorry about that. Ill look into what happened. But hopefully the example here has helped?

  • Dean Codemo

    Wouldn’t it also be possible to set an ng-model on each textbox and then set the ng-click handler on the button to call a controller function, say addBook(), which appends a new book to the array using the model values? (Or calling addBook(book, author, year) explicitly passing in the model values to the controller function because apparently that’s easier to unit test). No custom directives needed.
    I’m still learning angular so I never know what is the “best way” of doing something. I’m always tempted to put DOM manipulation code into the controller. What good is the controller then if not for DOM manipulation? Just to manage the model?

    • alvincrespo

      Hey Dean!

      Sorry for getting back to you so late. I’m trying to pick up my site again, so hopefully my comments here will help.

      Good points! With MVC or MVMM its always difficult to put code in the right place. Technically though, your controllers should “decorate” your model. Meaning that it should contain the logic that is missing from the model. In Angular, and in Ember, your DOM manipulation should never be in your controller.

      In Angular, you would want your DOM Manipulation to happen in the directive – giving your clear separation of concerns. Although you are correct in what you’re thinking – and I suggest you experiment, you should also make sure that you create and research best practices. This will help you in your architecture process and go a long way in helping you create reliable software.

      Cheers!