Blog

Prototype Angular UIs Without A Backend

vectors are fun

So youve got an AngularJS UI built out, but youll need a fleshed-out backend before being able to really take it for a test drive, right? Actually, it turns out, with the magic of Angular and its mocked $httpBackend, we dont need no stinking backend!

If youve heard of $httpBackend, youve probably heard of it it terms of writing unit tests. Tests that look like:

// tell http backend what to do when in gets a GET request at a specific URL$httpBackend.when(“GET”, “/users/4”).respond( {userName: “Doug”, userId: 4} );// Code we’re testinguserService.getUserById(4);// flush all pending requests (pretend the server just got back with response)$httpBackend.flush();expect(userService.getUser(4)).toBe( {userName: “Doug”, userId: 4});

While unit testing like this can help test and play with bits of code, it can’t drive an overall vision the way that seeing, clicking, and typing into a prototype can. Therefore, Ive taken $httpBackend and implemented a complete mock-up of my backend to allow me to use my UI and iterate quickly. Basically, I can now simply open file://path/to/my/project/index.html in my browser use my application as if it were backed by a database on a server. (That is as long as I dont fully reload the page:) ).

How have I done this? Read along with the corresponding jsfiddle

First, I tell Angular to use the angular-mocks $httpBackend (by passing it the $httpBackend constructor function) as a decorator on top of the concrete $httpBackend service. In Angular’s dependency injection, a decorator wraps the original service, layering on some custom functionality. In this case, Angular’s mock $httpBackend is setup to receive the concrete $httpBackend service, and will pass through to it if you use the passThrough() function after creating a rule (examples further down). In other words, I reserve the ability to pass some requests along as real HTTP requests.

myApp.config(function($provide) {    $provide.decorator('$httpBackend', createHttpBackendMock);});

All our code to mock the backend will take place in this function we execute before running the angular application:

myApp.run(function($httpBackend, $timeout) {        … });

In this run function, I can use the mock $httpBackend API to specify some rules, with actions to perform on the receipt of HTTP requests:

// Some statevar users = {};var userId = 0;$httpBackend.when('PUT', '/users').respond(function(method, url, data) {  data = angular.fromJson(data);  users[userId] = {userName: data.userName, userId: userId};  userId++;  return [200, users[data.userId]];});

I can then specify some rules based on a regex if need be:

var regexpUrl = function(regexp) {  return {    test: function(url) {      this.matches = url.match(regexp);      return this.matches && this.matches.length > 0;    }  };};$httpBackend.when('GET', regexpUrl(/users\/(\d+)/)).respond(function(method, url, data) {  data = angular.fromJson(data);  return [200, users[data.userId]];});

And since I’m using a delegate, I can decide that certain requests will just get passed through to do real requests (say JSONP requests to solr from my instant search directive!):

$httpBackend.when('JSONP', regexpUrl(/http:\/\/.*/))  .passThrough();

$httpBackends way of simulating an asynchronous response is for you to call flush() from your tests. So none of our apps requests will be responded to until we tell $httpBackend to flush. To create a kind-of $flush run-loop, we’ll use the angular $timeout service to call flush every half-second. $httpBackend still wants to be used in a unit testing context, so it will throw an exception if there’s nothing to flush. So we catch and discard that exception.

var flushBackend = function() {  try {    $httpBackend.flush();  } catch (err) {  // ignore that there's nothing to flush  }  $timeout(flushBackend, 500 /*ms*/);    };    $timeout(flushBackend, 500);

Viola, now I can experiment with my UI much more efficiently! Compared to other solutions, like mocking individual angular services or creating a mockable backend service that wraps the $http calls, I have found this solution the cleanest and least imposing on my production code.

Have you had to solve a similar problem? Please comment and let me know! Also, do you need help with rich search & discovery oriented user interfaces? Let us know. We know quite a bit about building rich UIs for Solr!