Node Angular Template

More for myself than anything, I created a base application template and put it up on GitHub. Feel free to fork it, and contributions are welcome. Here's what it has out of the box:

  • AngularJS 1.5
  • UI-Router
  • Bootstrap (LESS compiled)
  • Gulp Build
    • Lint
    • Transpile (ES6 -> ES5 with Babel)
    • Browserify
    • LESS
    • JS and CSS concatenation and minification
    • JS and CSS file versioning
    • and More!
  • JavaScript Unit Testing with Jasmine
  • Simple Node Server Script
  • Sample (Simple) AngularJS App with State Routing

I just needed something as a base to start other projects off of, so I threw this together as a starting point. You need Bower and NodeJS to get started, but all of the instructions are on the GitHub Page. Use it for your own projects, a quick look at AngularJS with ES6, or a sample of writing Gulp tasks.

All feedback encouraged and welcome.

ES2015, Promises, and Fun With Scope

I've been using Promises for some time now. JQuery has acted as a shim for some time, and several other libraries have been around as well. ES2015 includes Promises natively. If you're unfamiliar with Promises, I strongly suggest you read this great post by Dave Atchley.

Like I said though, I've been using Promises for a while now. So, when I started moving to ES2015 it was a bit of a kick in the pants to find issues with implementing my Promises. Let me give you an example of how something might've been written before:

view plain print about
1'use strict';
2    
3    module.exports = [
4        '$scope', 'orders', 'dataService',
5        function ($scope, orders, dataService) {
6            var self = this;
7            
8            self.orders = orders;
9            
10            self.addOrder = function (order) {
11                // ... do some stuff
12                // get original
13                dataService.get(order.id)
14                    .then(self._updateOrders)
15                    .catch(function (error) {
16                        // do something with the error
17                    });
18            };
19            
20            // faux private function, applied to 'this' for unit testing
21            self._updateOrders = function (order) {
22                // ... some process got our order index from orders, then...
23                orders[index] = $.extend(true, orders[index], order);
24            };
25        }
26    ];

Seems pretty straightforward, right? addOrder() gets called, which does some stuff and then retrieves an order from the service. When the service returns the order, that's passed to the _updateOrders() method, where it finds the correct item in the array and updates it (I know, it's flawed, but this is just an example to show the real problem).

So, what's the problem? That works great. Has for months (or even years). Why am I writing this post? Fair question. Let's take a look at refactoring this controller into an ES2015 class. Our first pass might look like this:

view plain print about
1'use strict';
2    
3    class MyController {
4        constructor ($scope, orders, dataService) {
5            this.$scope = $scope;
6            const myOrders = [];
7            this.orders = myOrders.push(orders);
8            this._dataService = dataService;
9        }
10        
11        addOrder (order) {
12            // ... do some stuff
13            // get original
14            this._dataService.get(order.id)
15                .then(this._updateOrders)
16                .catch(function (error) {
17                    // do something with the error
18                });
19        }
20        
21        _updateOrders (order) {
22            // ... some process got our order index from orders, then...
23            this.orders[index] = $.extend(true, this.orders[index], order);
24        }
25    }
26    
27    MyController.$inject = ['$scope', 'orders', 'dataService'];
28    
29    export {MyController};

That looks good, right? Well....

When MyController.addOrder() gets called, with this code, the get() method is called on the service, and... BOOM! Error. It says there is no _updateOrders() on this. What? What happened?

Well, it's not on your scope. Why? Because ES2015 has changed how scope is handled, especially within the context of a class. "this" is not the same in the context of the Promise's then(), at this point. But then, how are you supposed to reference other methods of your class?

Bom! Bom! BAAAAHHHHH! Use an arrow function. "Wait? What?" (lot's of confusion today) That's right, an arrow function. From MDN:

An arrow function expression (also known as fat arrow function) has a shorter syntax compared to function expressions and lexically bind the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous.

If you aren't still confused at this point you are a rockstar. Basically what it says is that this will become of the context from which you're using the arrow function. So, in terms of a Promise, if we change our addOrder() method like this:

view plain print about
1addOrder (order) {
2        // ... do some stuff
3        // get original
4        this._dataService.get(order.id)
5            .then((order) =>
this._updateOrders(order))
6            .catch(function (error) {
7                // do something with the error
8            });
9    }

This then fixes our this scoping problem within our then. Now, I know this isn't much in the way of an explanation into "How" it fixes it (other than setting the right this), and I know I'm not explaining what an arrow function is either. Hopefully this is enough to stop you from banging your head against the wall anymore, provides a solution, and gives you some clues on what to search for in additional information.

So, as always I welcome your feedback/suggestions/criticisms. Feel free to add a comment below, or drop me a direct line through the "contact" links on this page.

AngularUI Router and it's trasitionTo() Method

Angular applications are, typically, single page applications. So when you move around in an Angular application and an area (or areas) of the screen changes, you're said to be moving from one "state" to another. To simplify handling these states you can utilize the very powerful Angular-UI Router. From the GitHub site:

AngularUI Router is a routing framework for AngularJS, which allows you to organize the parts of your interface into a state machine. Unlike the $route service in the Angular ngRoute module, which is organized around URL routes, UI-Router is organized around states, which may optionally have routes, as well as other behavior, attached.

Yes, Angular's built in ngRoute module can give you the basics. AngularUI Router, on the other hand, provides the developer with a simple way of defining very complex state management. Or it seems simple, until...[WHAM!] you hit the brick wall.

The AngularUI Router is so easy, the documentation is... well, it's "OK". Sometimes you just run into a situation where the documentation is not clear enough. For instance, what if you're trying to call the same state, but with new parameters?

"What do you mean, Cutter?" Take the following example. A user is browsing a category in an online store. On the screen is a layout loaded with inventory of that category. Clicking on an item would bring up the detail of that item. After the detail of that item there are links to additional items, either related to the original, or of a similar nature. Seems pretty straightforward, right?

Wrong! (Well, not wrong, but....) It's not always so straightforward.Categories fall under the app, and inventory falls under a category. This says that we need a nested state approach. To illustrate, let me give you some basic routing here to kick it off.

view plain print about
1$stateProvider
2        .state('store', {
3            url: '/',
4            templateUrl: '/partials/store/index.html'
5        })
6        .state('store.category', {
7            url: '/{cat:\\d+}',
8            templateUrl: '/partials/categories/index.html'
9        })
10        .state('store.category.item', {
11            params: {itemid: null},
12            onEnter: function () {
13                // Do something here
14            }
15        });

The initial state ("store") is the site wrapper (navigation and footer and stuff). The category state would likely include some lookup of inventory for the category, and lay out that inventory on the page. The item state might open a modal for that item, with a title and image. It gets an itemid as a param, but for security reasons it is not in the url. There's also no 'view' associated with the item state, with the onEnter handling any UI changes.

So, what's the problem? Well, the issue is the additional inventory items that display below the item details. If a user is looking at an item, then chances are they're in the "store.category.item" state. The additional inventory links on this view would also link to the "store.category.item" state, with the difference being the itemid being passed. If the param were in the URL it might be different, but since it's intentionally being hidden the URL does not change. So a user clicking on a link with a ui-sref (for the same state they are currently in) just chokes. The same happens if your controller or service tries to call $state.go('same.state', params). You can add a reload option to your $state.go() call, but it will reload all the way up the parent state as well.

The requirement ran as followed:

  • Want to reload a child state, without reloading the parent
  • Want to change the parameters of that state
  • The (to be loaded) child state has no UI of it's own, no template, and no URL params

Initially I just tried something like this:

view plain print about
1<a ui-sref="store.category.item({itemid: item.id})">{{item.name}}</a>

It was really frustrating to find my click wasn't working, and took even longer to figure out it was because I was already in the "store.category.item" state. So then I tried something like this:

view plain print about
1<a ui-sref="store.category.item({itemid: item.id})" ui-sref-opts="{reload: true}">{{item.name}}</a>

But, of course, that would reload the entire state tree, parents on down. This was unacceptable. Finally, I found that I couldn't use the standard ui-sref bits to do what I needed to do. So, I ripped that out, and replaced it with an ngClick method in my controller:

view plain print about
1'use strict';
2    
3    class MyController {
4        // ... constructor and stuff, then...
5        
6        goToItem (itemid) {
7            this.$state.go('store.category.item', {itemid: itemid}, {inherit: true, notify: true});
8        }
9    }
10    
11    MyController.$inject = ['$state']'
12    
13    export {MyController};

view plain print about
1<a ng-click="ctrl.goToItem(item.id)">{{item.name}}</a>

Closer. I could set a break point and see that it was trying, at least. But still not right yet. I scoured the Guide for information, and then went looking through the API Reference, before finally stumbling upon $state.transitionTo().

The $state.transitionTo() method is used by $state.go(), under the hood. But, if you don't read through the documentation, it is easy to miss a little nugget in the description of the reload option:

reload (v0.2.5) - {boolean=false|string=|object=}, If true will force transition even if the state or params have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd use this when you want to force a reload when everything is the same, including search params. if String, then will reload the state with the name given in reload, and any children. if Object, then a stateObj is expected, will reload the state found in stateObj, and any children.

What??? That's not what the go() method's reload option does? In the go() method, the reload option is strictly a boolean. This says that, if I supply it with a string of the state to reload, that it would reload from that state down through it's children (if it were a parent of the one I was calling). By setting it to the same state I was trying to call, I ensured that it only reloads my item state, inheriting data from parent states. So, I tried it out...

view plain print about
1goToItem (itemid) {
2        let statename = 'store.category.item';
3        this.$state.transitionTo(statename, {itemid: itemid}, {reload: statename, inherit: true, notify: true});
4    }

Success! I could merrily bounce around among my inventory, reloading only the named state as I went. Glorious jubilation all around as the band played on (OK, going a little overboard here). Point is, it worked.

The AngularUI Router is immensely powerful, and can truly provide some complex (yet elegant) state management within your application. I highly recommend reading over the guide to see what might be available to you.

Angular, Data, and ES2015 Classes

I'm really loving the changes introduced in ES2015 (otherwise known as ES6 or the new JavaScript). At work we've transitioned to working in ES2015, and discovering the differences has been both fun and challenging, especially when it comes to changes in how variables are scoped. But we'll save some of that for another day, and talk for a moment about passing data around in an Angular application, and how you can have some fun with ES2015 classes.

In Angular, a Controller can have Services as dependencies. Services are singleton in nature, so a Service shared among multiple components can be used to "share" data, to a degree. First, let's create a simple controller with a few dependencies and a custom variable we want to track both within the controller, and among other bits of the app.

view plain print about
1'use strict';
2    
3    class MyController {
4        constructor ($scope, dataService, orderService) {
5            this.$scope = $scope;
6            this._dataSvc = dataService;
7            this._orderSvc = orderService;
8            
9            this.myCrazyVar = null;
10            dataService.myCrazyVar = this.myCrazyVar;
11        }
12    }
13    
14    myController.$inject = ['$scope', 'dataService', 'orderService'];
15    
16    export {MyController};

Next, let's create some simple method in the orderService that needs access to our custom variable. We can easily do this with the shared dataService:

view plain print about
1'use strict';
2    
3    class OrderService {
4        constructor (dataService) {
5            this._dataSvc = dataService;
6        }
7        
8        add (order) {
9            // get our custom var for quick reference
10            let forNow = this._dataSvc.myCrazyVar;
11            // do a bunch of stuff, then
12            forNow.OrderId = order.id;
13            // give our changes back to our dataService
14            this._dataSvc.myCrazyVar = forNow;
15        }
16    }
17    
18    orderService.$inject = ['dataService'];
19    
20    export {OrderService};

First, some might ask "Cutter, why didn't you just pass that variable into the method?" Well, sometimes you just can't. Others might ask "Isn't it passed by reference?" Scoping changes have adjusted how this works as well. Changing the variable in the service won't automatically update your controller variable. We'll talk more about that in a moment. Still other's might ask "What is 'let'?" That's a conversation about the differences in variable assignment in ES2015, and is really a discussion for another day. Ultimately I used let because I need the variable to be mutable.

But, to explain what I'm doing here, the add() method takes an order. What you can see of the method, it gets our custom variable and applies it to a local variable for easy reference. We update it with data from the order that was passed in. We then reset the Service property with the updated data.

OK, but that syntax with the Service property is odd, coming off of ES5. How does that work? Well, ES2015 classes allow for implicit getters and setters of properties. Consider the following:

view plain print about
1'use strict';
2    
3    class DataService {
4        constructor () {
5            this.myCrazyVar = null;
6        }
7    }
8    
9    export {DataService};

In the past, to access and change a property of an object would have required us to write some kind of getter or setter method. In this example, you can simply access and change the property directly through dot notation. But let's say you want to do something a bit more complex. There may be some bit of pre- or post-process you want to do, either when setting or getting the variable. For this, ES2015 classes allow you to define custom getters and setters:

view plain print about
1'use strict';
2    
3    class DataService {
4        constructor () {
5            this._myCrazyVar = null;
6        }
7        
8        set myCrazyVar (value) {
9            // I can do what I want in here
10            this._myCrazyVar = value;
11        }
12        
13        get myCrazyVar () {
14            // I could do stuff here too, if I wanted
15            return this._myCrazyVar;
16        }
17    }
18    
19    export {DataService};

Where this could come in handy is in that inter-app communication. A Controller can call methods on the Service, to set values and stuff, but the Service can't automatically pass data back to the Controller based on actions from other items accessing the Service (like our orderService interaction above). But, in Angular, we can use event handling and binding in this instance. First, let's put a listener on our controller:

view plain print about
1constructor ($scope, dataService, orderService) {
2        this.$scope = $scope;
3        this._dataSvc = dataService;
4        this._orderSvc = orderService;
5        
6        this.myCrazyVar = null;
7        this._dataSvc.myCrazyVar = this.myCrazyVar;
8        $scope.$on('crazyUpdated', ($event, newValue) =>
this.myCrazyVar = newValue);
9    }

Yes, the arrow function is a different concept for most front-end-only developers. I'm not going in depth on that here, but you can find plenty of info out there about them. The gist here is that if the crazyUpdated event is cast it will pass a new value, that we then use to update the Controller variable. This also tells us that myCrazyVar will always be changed from outside of the Controller. Now let's do some magic to make sure that event gets cast. In our dataService:

view plain print about
1'use strict';
2    
3    class DataService {
4        constructor ($rootScope) {
5            this._myCrazyVar = null;
6        }
7        
8        set myCrazyVar (value) {
9            this._myCrazyVar = value;
10            $rootScope.$broadcast('crazyUpdated', value);
11        }
12        
13        get myCrazyVar () {
14            // I could do stuff here too, if I wanted
15            return this._myCrazyVar;
16        }
17    }
18    
19    DataService.$inject = ['$rootScope'];
20    
21    export {DataService};

So we use our custom property setter. When the value is changed, it automatically broadcasts that change. The Controller then picks up that change, and applies it to it's own internal variable.

So, what have we learned here? Well, first there's some samples on using ES2015 classes as Controllers and Services within Angular. Simple examples, but there you go. Next, we talked about class property getters and setters, both implicit (no need to define, they just work), and explicit. Our explicit example shows where you can apply some additional logic during those processes. This may not be the greatest example, but it shows you that you can do stuff. Probably the greatest usages here will be in data validation, or in splitting concatenated data (like a full name to first and last, for example).

Using those ES6 classes in your app is a matter of importing the classes into your app:

view plain print about
1import {MyController} from './MyController';
2    import {DataService} from './DataService';
3    import {OrderService} from './OrderService';
4    
5    // ... other app init stuff
6    .controller('MyController', MyController)
7    .service('dataService', DataService)
8    .service('orderService', OrderService);

So, it's still all new to me, these changes to scope and classes and the like. But it's fun, and powerful, and has a ton of possibilities. If I screwed something up just let me know. All feedback is welcome.

AngularJS, Jasmine Testing, and Mocking Global Objects

So, you've made the jump to writing tests for your code. You've discovered the value of TDD, and even integrated it into your CI process. Awesome! Well, until your tests fail. Then you bang your head against your desk, trying to figure out how to test "this" scenario, and get totally frustrated by the lack of information. Makes you want to quit writing code and start driving the little cart around the driving range that picks up the golf balls.

We've all been there. This morning in fact. Don't give up, you'll figure it out soon enough. For example, you're trying to test a new method in your code. Within this new method you're referencing an object that's on the global scope. But, of course, you get a 'fail' every time, because your test doesn't know what that object is.

The answer is to mock the object. But how do you mock an object on the global scope? Really, it's easier than you might think. Just decorate your window.

Within your root 'describe' block, you probably have a test global 'beforeEach()' where you're mocking your module. Here, you can create a 'decorator' for Angular's '$window', and attach a mock of your object.

view plain print about
1beforeEach(angular.mock.module('myModule', function ($provide) {
2        $provide.decorator('$window', function ($delegate) {
3            $delegate.foo = {
4                bar: function () {
5                    // just for test
6                }
7            };
8            return $delegate;
9        });
10    }));

Then later, when your tests hit code that references that global 'foo' object, your tests won't bomb. Further, you can attach spies to see that object methods are being called in your test

view plain print about
1it ('myMethod', function () {
2        spyOn(window.foo, 'bar').andReturn(true);
3
4        ctrl.myMethod();
5        $scope.$digest();
6
7        expect(window.foo.bar).toHaveBeenCalled();
8    });

When the test is run, and the 'myMethod()' function is called on your controller, which internally calls 'foo.bar()', the test will see that and resolve it with the 'andReturn()' value you supplied.

This is invaluable when you're writing tests for those areas of your app where you're calling the Google API's, or some script that dynamically embeds a video player or something. Your test isn't worried about their code (that's something else all together). Your test is about knowing that your code does what it needs to do.

As they say, "there's usually more than one way to skin a cat" (I'm really going to have to find out where that coloquial nugget came from), so if you know of another way to perform the same test scenario, please share.

2014 in Review: A Year of Learning

2014 has been an outstanding year for me, in many ways, but perhaps one of the most important things (besides my family) has been continuing to do what I love. I'm passionate about development, and constantly working to learn new things. This is important for any developer, as our world changes so quickly today. New standards, new languages, new frameworks, it's a consistent onslaught of new ideas, and impossible to learn each and every one, but important to get exposure none-the-less.

The early part of the year I was still maintaining a large legacy application. We were in the final stages of migrating some very old pieces of the application into new framework architecture (FW/1) along with a new look (based on Bootstrap 3). When you're working on legacy applications, there are rarely opportunities to dive in to new things, so that project was a nice nudge to explore areas previously untouched. Back in April, though, I took on a new position that had me doing nothing but non-stop new product development. Not only was this a great switch, but the particular tasks I was given had me working with technologies with which I had little or no exposure, and often without a team peer who could mentor me, as many of the technologies were new for the company as well.

Historically, I'm a server-side programmer. But, over the last several years, I've spent a great deal of time honing my client-side skills. I'm no master, by any means, but I've consistently improved my front-end work over that time, and 2014 built upon that considerably as well.

One area I could get some mentoring in was AngularJS. This was a big shift for me, and while I am still learning more and more every day, it has been an exciting change up for me. Angular is extremely powerful and flexible, taking some hard things and making them dead simple (to be fair, it makes some simple things hard too ;) ). Angular is one of those items I wished I had spent more time with a year or so back, because in hind-sight it could have saved me hundreds of hours of work. I'm really looking forward to working more with Angular.

From a software craftsmanship standpoint, I also had to dive in to a slew of technologies to assist in my day-to-day development. I now use Vagrant to spin up my local dev environment, which is a godsend. One quick command line entry, and I'm up and running with a fully configured environment. I went from playing around with NodeJS to working with it day in and day out, writing my own plugins (or tweaking existing ones), and to using (and writing/tweaking) both Grunt and Gulp task runners for various automation and build tasks. To take something as "source" and convert it to "app" with a simple command line entry is the shiznit. How many hours did I waste building my own sprites, and compiling LESS in some app? Now it happens at the touch of a few keys.

Then there are the deep areas that some project might take you. I had to dust off year's old AS3 skills to refactor a Flash based mic recorder. There was some extreme study into cross-browser client-side event handling. Iron.io has a terrific product for queuing up remote service containers for running small, process intensive jobs in concurrency without taxing your application's resources. That lead into studies in Ruby, shell scripting, and Debian package deployment (not in any sort of order), as well as spinning up NodeJS http servers with Express.

Did you know that you could write automated testing of browser behavior by using a headless page renderer like PhantomJS? Load a page, perform some actions, and record your findings, it really is incredibly powerful. It also has some hidden 'issues' to contend with as well, but it's well worth looking into, as the unit testing applications are excellent. Then you might change direction and checkout everything there is to know about Aspect Ratio. It's something you should know more about, when thinking about resizing images or video.

(Did I also mention that I went from Windows to Mac, on my desktop, and Windows to Linux, on my dev server? Best moves I ever made!)

Speaking of video, I got the opportunity to go beyond the basics with ffmpeg's video transcoding. For those unfamiliar with the process, you write a command line entry defining what you want. Basically it's one command with 200+ possible flags in thousands of possible combinations, and no clear documentation, by any one source, on how to get exactly what you want (read: a lot of reading and a lot of trial and error).

If that had been all of it, it would have been a lot, but then I really got to have fun, and got to rewrite a Chrome Extension. Now, I had the advantage that we already had an extension, but I was tasked with heavily expanding on it's capabilities, and it's been a blast. Going from something relatively simple to something much more complex is always a challenge, but doing so when you don't fully grasp the tech at hand is even more challenging. Google has created a brilliant triple tier architecture for interfacing the browser 'chrome' with the pages inside them, and developing advanced extensions with injected page applications has a lot of twists and turns along the way. I've learned enough along the way that I'm considering writing a presention on this process for the upcoming conference season.

So, in retrospect, I went from maintaining a large legacy system to doing cutting edge custom development, learning something new each and every day. Awesome! Now, the downside to this sort of process is that you lose valuable productivity time getting through the learning curve. It's difficult to make hard estimates on tasks that no one else has done before, and measuring success is taken in baby steps. But the upside is that you are constantly engaged, constantly motivated, and those skills will eventually translate into better products down the road. Products that won't incur that same learning curve (not to mention the new ideas that can come from exposure to so many different technologies). I can't claim mastery of any of it, yet, but did gain a solid foundation in most of it, and it just makes me hungry for more.

So, if I have one wish for 2015 for you, dear reader, as well as myself, it is only that you continue to learn every day. Maybe not to the levels described above (for I am a bit of a lunatic), but at least take the chance to branch out into one new area of development in the coming year.

Angular UI Sortable for Managing Sort Order

In my newest position I've been learning AngularJS. After diving in for a little over two months now, I'm seriously loving it, and wondering why I didn't seriously look at it much sooner.

One of the things it has taken a bit to get used to is that Angular does some things differently. I love Bootstrap but, like all other plugins, Angular's MVC approach has you do things "the Angular way", which can take a bit to break old habits. Luckily for us there are projects like Angular UI to help take the pain out of these sort of things.

One of the very few things that I use jQueryUI for is it's Sortable, Draggable and Droppable plugins. Angular UI has modules for these too. In this example, we're going to use the Sortable plugin for easy Drag n Drop display order management.

Using Drag n Drop for display order management is a fairly standard task. In the old days, we used up and down arrows that would typically be links, firing calls back to the server for database interaction and the refreshing the page for the new order. This was long, tedious, and a horrible user experience. Drag n Drop takes care of that. It allows the user to quickly define the order of things prior to ever sending data back to the server.

This demo isn't going to get into the server side mechanics of any of this, but rather the client-side stuff. First we'll start with the Bootstrap sample template (we'll use the Bootstrap CSS for basic layout stuff in our example). Don't forget to take out the Bootstrap JS file, and we're going to load the jQuery file up at the top ("why" will become clearer later).

view plain print about
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <title>Bootstrap 101 Template</title>
8
9 <!-- Bootstrap -->
10 <link href="css/bootstrap.min.css" rel="stylesheet">
11
12 <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
13 <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
14 <!--[if lt IE 9]>
15 <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
16 <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
17 <![endif]-->

18
19 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
20 </head>
21 <body>
22 <h1>Hello, world!</h1>
23
24 </body>
25</html>

A requirement of the Angular UI Sortable plugin is the actual jQueryUI sortable plugin, and it's dependencies. Luckily we can download a really trim file, giving us only what we need. We will insert the JS file in our document head, directly after our jQuery file.

view plain print about
1<script src="/path/to/file/jquery-ui.min.js"></script>

After that, we drop in the CDN link for AngularJS:

view plain print about
1<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.14/angular.min.js"></script>

And then we need the Angular UI sortable. I haven't found a CDN for this one, so you'll have to install it locally:

view plain print about
1<script src="/path/to/file/ui-sortable.js"></script>

And finally, let's create a file to put all of our application logic:

view plain print about
1<script src="/path/to/file/sortable-app.js"></script>

In review, and inventory of our code show us that:

  • We're loading the Bootstrap CSS
  • We're loading some shivs, for handling HTML 5 stuff in IE 9 or earlier (good luck with that)
  • We're loading jQuery, and our chopped down version of jQueryUI
  • We're loading Angular, the Angular UI Sortable plugin, and a file for our application

Our app file is empty right now. Let's get it started by initializing our Angular application. We do this by implementing our app as an Angular module:

view plain print about
1angular.module( "sortable", [ "ui.sortable" ] )

Basically this says our app is called "sortable", and it has a dependency on "ui.sortable". That part was easy enough, but now we have to associate the app with our display. We do this by modifying our opening html tag:

view plain print about
1<html lang="en" ng-app="sortable">

The next thing I'll do is create a controller for the next part of our app.

view plain print about
1.controller( "ListController", function ( $scope ) {
2
3})

view plain print about
1<body class="container" ng-controller="ListController">

We created a controller, and associated it with the body of our document. You can assign a controller to any element within your document, but this is a pretty simple example, so we're going simple here.

We need some data. Ordinarily you'd probably pull some data from the server, but for our demo we'll just create an array in our controller. The variable will be within the controller's scope, making it available to the block of our view within the ngController's elements.

view plain print about
1$scope.bands = [
2 {
3     name: "The Beatles",
4     rank: 1,
5     mode: "view"
6 },
7 {
8     name: "The Who",
9     rank: 2,
10     mode: "view"
11 },
12 {
13     name: "The Jimi Hendrix Experience",
14     rank: 3,
15     mode: "view"
16 },
17 {
18     name: "Led Zepplin",
19     rank: 4,
20     mode: "view"
21 },
22 {
23     name: "The Doors",
24     rank: 5,
25     mode: "view"
26 }
27];

view plain print about
1<body class="container" ng-controller="ListController">
2
3<!--- The "bands" are available inside this block -->
4
5</body>

Now that we can access our controller's variables, let's go ahead and output them to our screen:

view plain print about
1<div class="container-fluid">
2     <div ng-repeat="band in bands">
3         <div ng-if="band.mode == 'view'" class="row well">
4             <div class="col-xs-2">
5                 {{band.rank}}
6             </div>
7             <div class="col-xs-10">
8                 {{band.name}}
9             </div>
10         </div>
11         <div ng-if="band.mode == 'edit'" class="row">
12             <div class="col-xs-10">
13                 <input type="text" class="form-control" ng-model="band.name" placeholder="Band Name...">
14             </div>
15             <div class="col-xs-2 text-right">
16                 <button type="button" class="btn btn-primary" ng-click="addBand( band )">Add</button>
17             </div>
18         </div>
19     </div>
20 </div>
21 <hr>
22 <div class="text-right">
23     <button ng-click="createBand()" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span></button>
24 </div>

OK, I need to slow down a minute. Let's explain some code here. First, I loop over the bands for output. If it's in "view" mode, it shows you the rank, and the name. If it's in "edit" mode, then you see a text box and a button. Lastly, there's a button at the bottom for adding new bands.

There are two methods tied in here. Let's add them both to our controller:

view plain print about
1$scope.createBand = function () {
2    
3    var band = {
4        name: "",
5        rank: $scope.bands.length,
6        mode: "edit"
7    };
8    $scope.bands.push( band );
9    
10};
11
12$scope.addBand = function ( band ) {
13    
14    band.mode = "view";
15    
16};

Nothing crazy here. We aren't doing any server-side stuff in this demo, so all we're trying to say here is "create" will put a stub object last in our bands array, while "add" will change a step's mode from "edit" to "view".

You can try that out now, if you want. Clicking on the "Plus" button at the bottom will add a new line for you. Type in a Band Name and then click that line's "Add" button. Each time you hit "Add", the mode changes to "view", and you see the display order followed by the Band name. You can add as many bands as you like, but so far we still can't re-order them, and anyone who knows me knows that as much as I love The Beatles, Jimi is number one in my book.

You'll remember that we already have the Angular UI Sortable plugin available to us. We included the code, and the dependency, now we just need to implement it. On the div that contains our ngRepeat loop directive, just add the directive call for the Angular UI Sortable:

view plain print about
1<div ui-sortable ng-model="bands" class="container-fluid">

This adds the directive to the container, as well as defining the model involved. ngModel is critical here, or it won't work. If you reload your page right now, you'll find that you can Drag n Drop rows backwards and forwards with ease. But you're not done. Just for grins, hit your "Plus" button for a form row, then try to drag it. It's not easy to grab it, but it can be done. Suddenly you're trying to re-order stuff that isn't ready for re-order!

This is easily bypassed, by adding some configuration to your sortable. The jQueryUI Sortable has it's own API and configuration options, and you can easily apply them to your Angular UI Sortable as well. First, create a configuration object in your controller. We don't need much, just something that identifies what our drag handles are (if there's no handle, it can't be dragged):

view plain print about
1$scope.sortConfig = {
2        
3    handle: ".well"
4        
5};

We aren't applying the well class to edit rows, so this will exclude them. All we have to do is tell our directive to use this config:

view plain print about
1<div ui-sortable="sortConfig" ng-model="bands" class="container-fluid">

Now, if you reload and try it again, you'll see that you can no longer Drag n Drop the edit rows.

But, as you continue to drag items around, you'll notice that your display order column of data is way out of wack after a bit. The order should still be "1, 2, 3, etc", but now it's just a jumble. You know that the output is tied to your model, by accessing the band.rank, so as items get dragged around the rank needs to get updated. This can easily be done by watching the bands variable in the controller. We can set a method that says "when this changes, update it":

view plain print about
1$scope.$watch(
2    "bands",
3    function ( newValue, oldValue ) {
4        
5        for ( var i = 0; i < newValue.length; i++ ) {
6            
7            newValue[i].rank = i+1;
8            
9        }
10        
11    },
12    true
13);

The $watch function allows you to set model listeners. Here we are looking for any change to the bands array in the $scope. Since bands is a complex data type we include the "true" to match objectEquality, meaning it will watch it "deeply", for any change, and fire when it changes. We're looping every item in the array, and resetting the rank according to it's new position in the array. The rank may never have changed (each time a new row is added this will also fire), but the overhead is pretty minimal, and it's a simple way to ensure the display order is always right.

That's it. Sample Code can be pulled from the download link. If you have questions, comments, or war stories, just hit me with a comment below.