AngularJS: the beauty of concision

Some of you might remember a Backbone blogging engine I made a while back. It certainly wasn’t the most advanced use case for Backbone, but I think that it did a decent job of elucidating some of Backbone’s features: event-driven responsiveness, templating, collections, and so on. It was also a great learning experience and my first foray into thick client-style development.

But then a few weeks ago, a number of trusted friends and colleagues began raving about AngularJS. Curious about what the fuss was about, I began doing some exploring, looking at sample apps, reading the API docs, and watching a few videos, and it became abundantly clear that Angular is an almost shockingly powerful library. I was surprised by the kinds of heavy lifting that can be accomplished with little effort. And so I set out to see how concisely I could re-implement my Backbone project in Angular.

I was quite pleased with the result.

Getting started

The first thing you need to do is specify within your <html> tag itself–I know, crazy, right?–that your HTML page is going to be staging an Angular app. Instead of the typical <html> tag, you need to insert a <html ng-app> tag instead. As you’ll see over the course of this tutorial, you’ll be embedding a surprising amount of basic application logic in HTML tags themselves when you’re using Angular.

Now, I need to specify my document head:

   <Title>AngularJS Blog Engine</title>
   <script src="">
   <script src="">
   <script src="blog.js">
   <link rel="stylesheet" href="style.css"">

Notice that AngularJS has zero dependencies (not even jQuery or Underscore), which is pretty impressive in itself. I’ll also be importing the DateFormat library for making the timestamps on my blog posts look nice. The blog.js file will harbor my application logic.

Setting up a controller

When I was putting together my Backbone blog engine, I didn’t have anything resembling a controller. That was okay because all of my controller-style logic was placed elsewhere, namely in my views, models, and overarching collection. Like Backbone, Angular doesn’t force you to specify a controller, but there are cases where I would highly recommend implementing one. In my case, I have a well-specified resource (blog posts) that have methods attached, which makes a controller a very nice thing to have.

Here’s what my controller (in my blog.js file) looks like:

function BlogController($scope) {
  $scope.posts = [];
  $scope.addPost = function() {
    var now = new Date();
    var time = dateFormat(now, "dddd, mmmm dS, yyyy, h:MM TT Z");
  $scope.posts.push({ title: $scope.postTitle,
                      content: $scope.postContent,
                      time: time,
                      backgroundColor: $scope.backgroundColor });

So, let us take stock of what’s going on here. The most important thing to be aware of is the $scope variable. This variable acts as a kind pub/sub store for everything involving our controller. I will store specific blog posts (as hashes/objects) in this $scope.posts variable. When I add a post new with the addPost() function, I’ll snatch the current time with the var now = new Date(); expression and then push a new post to the (currently empty) $scope.posts array.

Each post will consist of four key/value pairs, specifying a title, content, formatted timestamp, and background color. We’ll see in a minute how those values will be passed into the controller, as that logic will take place in our HTML file.

Wiring our HTML to let the Angular goodness flow through

Like I said before, part of the magic of Angular is that it relies on logic embedded within HTML tags (as in KnockoutJS). One of the things that I love about Angular is that I can attach a controller to a specific DOM element and restrict its scope to that element alone. I’ll do that by inserting a <div ng-controller="BlogController"> tag into my document. Everything I do in this tutorial will transpire within that div element, although an app could easily have multiple controllers attached to their own respective DOM elements. Angular is built for this.

Within my new div element, hotwired to sync up with my BlogController, I’ll start with an unordered list for displaying my posts:

  <li style="background-color: {{ post.backgroundColor }};" ng-repeat="post in posts">
    <h2>{{ post.title }}</h2>
    <p>{{ post.content }}>/p>
    <p>{{ post.time }}</p>

The ng-repeat="post in posts" in the <li> tag means that a new list item will be produced for every post in the posts array within my controller. The background color, title, content, and time will be inserted into this template automatically. There is simply no way to do this so concisely in Backbone, where you would need to hook up each post view to a template in your HTML file and then pass it into a collection that is then rendered (correct me in the comments if I’m wrong!).

For good measure, I also want to display how many posts have been made to the blog thus far. This can also be done incredibly neatly:

<h5>Total posts: <strong>{{ posts.length }}</strong></h5>

This kind of syntax should be familiar to anyone who has used Mustache. The most important thing to remember here is that if we placed this <h5> tag outside of the div element tied to our Angular controller, the {{ posts.length }} variable would come up empty-handed. It simply wouldn’t know where to look for this value.

Time to input some data and close the loop

So far, we can’t do much because we can’t input data. Our posts array is empty and there’s not much we can do about that. So let’s create a form and get to work:

<form ng-submit="addPost()">
  <input type="text" ng-model="postTitle" placeholder="Post title"><br />
  <input type="text" ng-model="postContent" placeholder="Post content">
  <select ng-model="backgroundColor">
    <option value="teal">Teal</option>
    <option value="beige">Beige</option>
    <option value="white">White</option>
  <input type="submit" value="Submit post">

The ng-submit="addPost()" embedded in the <form> tag specifies that the addPost() function from our BlogController will be fired upon commit. Remember from our controller that the values for new posts are tied to variables in our div. When we insert an ng-model snippet into an HTML element, that ties the input value to our $scope variable and allows that input to be passed through our controller. I find this to be a much more elegant way of doing things than the standard jQuery $('#element').val(), which is required in Backbone and other libraries.

And that’s it. Everything that was on offer in my Backbone blog engine is on offer here, and all of it implemented in shockingly few lines of code.

Thoughts about Angular and Backbone

As much as I still like Backbone and think that it was a crucial stepping stone toward other libraries, I can’t help but think that its days in the sun might soon come to an end. If the maintainers of Backbone can’t find a way to make client-side code this concise, then we might be looking at a gradual sea change in the direction of frameworks like AngularJS.