January 29, 2013

AngularJS

After reading the recent article Step by step from jQuery to Backbone, I was a bit put off about how the 16 lines of readable jQuery was transformed into 43 lines of mostly incomprehensible Backbone. Something struck me about it: it often seemed to be abstraction for the sake of abstraction, with few benefits. I had recently begun playing with AngularJS, and wanted to do a similar tutorial. I'll start with the same jQuery, and transform it into Angular code.

In a way, this is comparing apples to oranges. The authors of the Backbone version took the refactoring to the extremes. I'm more interested in creating a simplified version, to show how quickly you can use Angular to make a relatively advanced app.

Note: The Angular code in this tutorial is heavily taken from the tutorials on AngularJS.org, which is completely worth checking out.

Original jQuery

We've started with the same exact code as the original article, with a few slight modifications:

  1. The HTML is important in Angular, so we're using JSFiddle to show both HTML and JS (as well as a demo).
  2. In order to make the code run-able, we had to make some small modifications to work with JSFiddle's AJAX API.

Let's make things more interesting

As it turns out, this original example is too trivial to really show off the power of Angular. We want to show off some cool Angular features, so let's turn this into a todo list. (And, for those of you who have followed the tutorials on AngularJS.org, you know my secret motive: their main example is a todo list.)

Let's add a checkbox before each item, as well as a counter of remaining tasks at the top. We also want to append "Done!" to the end of each completed task. While admittedly not the most optimized jQuery example, you can still note how much DOM manipulation is required for these relatively simple tasks.

Setting up an Angular Controller and a repeater

From here on out, make sure you look at the HTML. While jQuery or Backbone don't really need HTML to illustrate their core concepts, Angular moves most of the logic into the HTML.

We start by adding ng-app to the parent-most div, to indicate the active portion of the app. Most apps would put this on the <html> tag, however we'll put it on a wrapper div for the sake of JSFiddle.

Next, we define the controller by adding ng-controller="TodoCtrl" to the section of the page we want to work with. The naming is unimportant, however it conventionally ends with "Controller" or "Ctrl". It just has to match up with the name of a function in our JS.

In the JavaScript, we define TodoCtrl. Note how we pass in $scope as the first paramater. We also define $scope.todos, which could be seeded with data if we wanted to. (I tend to have an AJAX call here to get existing data out of the database.)

We've given the checkbox a ng-model, which we'll talk about in a bit.

Next, we're going to add a repeater into the HTML. Any changes we make to $scope.todos will be reflected in the HTML.

A repeater is basically just a foreach loop, embedded right into the HTML. We create one <li ng-repeat='todo in todos'>, which will be used as the template for everything in $scope.todos.

We can then reference todo inside the looping element: for example, we do {{todo.text}} to print out the text.

Think of these as templates: except rather than writing HTML inside the JS files, we write them right in the HTML &mdas; arguably where it should be.

Note: Unfortunately, this demo doesn't work since we're only halfway there.

Implementing the Form

At this point, we'll completely remove the form submission from the jQuery version, and replace it with our own.

We put a ng-submit="addTodo()" into the HTML, and define it in the JavaScript as $scope.addTodo()

We can add a new <li> simply by pushing it to the $scope.todos array.

Note how we don't have to do $('#new-status').val() to get/set the value of that text box. We've bound it to $scope.todoText by giving it a ng-model="todoText". If you update either the variable or the input field, the changes will be synced.

Note how before, we gave each checkbox in the repeater a ng-model. This tells angular that the value of the checkbox should mirror todo.done. If we were to update todo.done in the JavaScript, the checkbox would become checked. If we toggle the checkbox, the value of todo.done stays in sync in the JavaScript.

Update remaining Todos

We're already come pretty far, with minimal JavaScript. To add a count of the remaining todos, we just put {{ remaining() }} right into the HTML. Angular will update it whenever a new todo is added.

The remaining() implementation is relatively straightforward.

Show "Done!" on checked items

Almost the same as above, but inside the repeater.

Talking to the server

Okay, you caught me: I left out the AJAX part. We could have used Angular's $http to quickly do AJAX requests, however it's really just a watered down version of jQuery's $ajax. So, instead, check out the official tutorial: Wire up a Backend. It uses MongoLab to keep the page in sync with the server.

Other awesome stuff

Let's look at some other cool things we can do with only a few lines of code.

First, we're going to order the todos alphabetically by adding | orderBy:'text' to the repeatable. With a minor HTML change and no changes to our JavaScript, we can order our list however we please — in this case, alphabetically by 'text'.

Next, we're going to make the list filterable by "all", "done" and "not done". We simply define $scope.filterCriteria={} in the JS, add | filter:filterCriteria to the repeatable, and add links with the approperiate variant of ng-click="filterCriteria.done=true" as an attribute. With just a few lines of code, we have a filter that works like a charm.

In conclusion…

When learning Backbone (and again when reading the article in question), I couldn't shake the feeling that Backbone was often abstraction for the sake of abstraction. It never really made my code easier to write or read; it simply gave me a skeleton for how things could be abstracted.

I also couldn't shake the feeling that the jQuery could presented would be better reimplemented in Angular.

The point of this article isn't to give a fair, one-to-one comparison of syntax or line count between Backbone and Angular. Rather, the point is to show off how different the approaches of the two frameworks are. Angular's two way data binding is nothing short of magical. Note that not once did we manually touch the DOM. In fact, if you're touching the DOM at any point, you're probably doing something wrong. Rather, you update the "models" (in this example, just an array of JSON) and everything is kept in sync.

Naturally, this is a pretty basic implementation: I wanted to show off what Angular can do, not necessarily to show off best practices. Things get a little more complicated when you, say, start building a real app that syncs data to a server.

Hopefully this quick introduction to Angular piqued your interest. Check out AngularJS.org for much more complete tutorials.

About Gregory Koberger

I'm a freelance developer and designer, formerly of Mozilla. I talk a lot about web development, technology and user experience — sometimes on my blog but mostly on Twitter.

Keep Reading

Your Turn