Monday, April 22, 2013

An example AngularJS app with Google App Engine RESTful Web Services

So I'm fortunate enough to work with people that are constantly pushing for us to learn new technologies.  I released a pretty niche Android app maybe 2 weeks ago for Exposure101 - that aside I've had to start learning AngularJS at my real job over the last week.  Over the last year or two I've been working pretty heavily with GWT - and in all honesty had never really done any type of JavaScript - just doing Angular for these last few days has completely sold me on it.

I put together a pretty simple App to show the basics of Angular and how to setup a simple Web Service environment.  I'm a huge fan of Google App Engine - it's really simple to use and you can stand up an entire backend in 20 minutes once you've done it before.  I'm gonna start with the server first and move on to Angular after.

Before we start you'll need the following libraries (versions are just the ones I used) - Make a new App Engine project (for this example my project name is "angular-demo" with the package "com.exposure101.angular.demo") and copy them into your war/WEB-INF/lib directory.


I try to avoid XML configurations and Guice has a really nice way of helping you to setup your server.  The first thing to do is open up your web.xml, clear everything between the web-app tags and setup the Guice Filter and Servlet Context Listener as follows:

 <!-- Servlets -->
  <listener>
    <listener-class>com.exposure101.angular.demo.server.AngularDemoServletContextListener</listener-class>
  </listener>

  <filter>
    <filter-name>guiceFilter</filter-name>
    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>guiceFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>Angular_demo.html</welcome-file>
  </welcome-file-list>

  <!-- generated by app engine - if you delete it it will come back -->
  <servlet>
    <servlet-name>SystemServiceServlet</servlet-name>
    <servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
    <init-param>
      <param-name>services</param-name>
      <param-value />
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>SystemServiceServlet</servlet-name>
    <url-pattern>/_ah/spi/*</url-pattern>
  </servlet-mapping>

The web.xml and the next 3 classes are basically boilerplate server side Guice. If you're not familiar this I would highly advise you go check it out - it's pretty awesome.  At a really high level here's what each one does:
  • ServletContextListener - provides all the modules you plan to inject (in our case it will only be 2)
  • ServletModule - this is now your web.xml - you define your servlets and filters in here
  • AbstractModule - this binds the classes that will be injected through your app.

Here's the ServletContextListener:

package com.exposure101.angular.demo.server;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;

public class AngularDemoServletContextListener extends GuiceServletContextListener {

  @Override
  protected Injector getInjector() {
    return Guice.createInjector(new AngularDemoModule(), new AngularDemoServletModule());
  }
}


The ServletContextListener's pretty simple - Here's the ServletModule:

package com.exposure101.angular.demo.server;

import java.util.HashMap;
import java.util.Map;

import com.google.inject.servlet.ServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;

public class AngularDemoServletModule extends ServletModule {

  @Override
  protected void configureServlets() {
    final Map<String, String> params = new HashMap<String, String>();
    params.put("javax.ws.rs.Application",
        "com.exposure101.angular.demo.server.AngularDemoRestApplication");
    serve("/rest/*").with(GuiceContainer.class, params);
  }
}


The ServletModule does a little bit more.  REST Web Service classes (I've been calling them Providers for forever - I've seen people call them Providers and Services - I'm not sure if there's a standard naming convention - if I broke it I'm sorry) are kicked off as Singletons during deployment via an Application class.  Whenever a call is made to our server's rest path it will be served with the Application class passed in above.  Since we're serving it up through the GuiceContainer we can inject any dependencies into it through our implemented AbstractModule:

package com.exposure101.angular.demo.server;

import com.exposure101.angular.demo.server.dao.Dao;
import com.exposure101.angular.demo.server.dao.PersonDao;
import com.exposure101.angular.demo.server.model.Person;
import com.exposure101.angular.demo.server.services.AbstractProvider;
import com.exposure101.angular.demo.server.services.PersonProvider;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;

public class AngularDemoModule extends AbstractModule {

  @Override
  protected void configure() {
    bind(new TypeLiteral<Dao<Person>>() {}).to(PersonDao.class);
    bind(new TypeLiteral<AbstractProvider<Person>>() {}).to(PersonProvider.class);
  }
}

These classes haven't been implemented yet - but basically any time an @Inject is used for a Dao<Person> or an AbstractProvider<Person>, the bound PersonDao or PersonProvider will be injected for you.  For this project the Guice is definitely overkill - but I've used it in every project I've worked on in the last year and I'm really trying to push it on you.

The last important class here is the actual Application class:

package com.exposure101.angular.demo.server;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.inject.Inject;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;

import com.exposure101.angular.demo.server.model.Person;
import com.exposure101.angular.demo.server.services.AbstractProvider;

@Path("/rest")
public class AngularDemoRestApplication extends Application {

  @Inject
  AbstractProvider<Person> personService;
  
  @Override
  public Set<Object> getSingletons() {
    return new HashSet<Object>(Arrays.asList(new Object[] { personService }));
  }
}


Since there's only 1 provider this class seems pretty trivial, if there were more you would bind them in your Module and inject them in the same way.  Now that the boilerplate code is up you're just going to build out the rest of the backend.  All you have to do is create the Person model, a DAO to interface with the datastore (Objectify is hands down The best framework for App Engine's datastore) and the actual Web Service Provider for your Angular app to query against.  I'm just going to lay these out in bulk here.

Here's the actual Person Model:

package com.exposure101.angular.demo.server.model;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PrePersist;

import com.googlecode.objectify.annotation.Indexed;
import com.googlecode.objectify.annotation.Unindexed;

@Entity
public class Person {

  @Id
  private Long id;
  
  @Unindexed
  private Integer version;
  
  @Indexed
  private String firstName;
  
  @Indexed
  private String lastName;
  
  @Indexed
  private String ssn;
  
  @Indexed
  private String gender;
  
  @Indexed
  private String dateOfBirth;
  
  public Person() {
  }

  public Long getId() {
    return id;
  }

  public Integer getVersion() {
    return version;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getSsn() {
    return ssn;
  }

  public String getGender() {
    return gender;
  }

  public String getDateOfBirth() {
    return dateOfBirth;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public void setVersion(Integer version) {
    this.version = version;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public void setSsn(String ssn) {
    this.ssn = ssn;
  }

  public void setGender(String gender) {
    this.gender = gender;
  }

  public void setDateOfBirth(String dateOfBirth) {
    this.dateOfBirth = dateOfBirth;
  }
  
  @PrePersist
  public void updateVersion() {
    if (version == null) {
      version = Integer.valueOf(0);
    }
    version++;
  }
}



Here's The Objectify classes:

The DAO interface:

package com.exposure101.angular.demo.server.dao;

import java.util.List;

import com.googlecode.objectify.Key;

public interface Dao<T> {

  T find(Long id);

  T find(String id);

  T find(Key<T> key);

  Key<T> key(T t);

  List<T> findAll();

  List<T> findAll(List<Key<T>> keys);

  List<Key<T>> key(List<T> list);

  T persist(T t);

  void delete(T t);
}


The Abstract DAO:

package com.exposure101.angular.demo.server.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.exposure101.angular.demo.server.model.Person;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.util.DAOBase;

public abstract class AbstractDao<T> extends DAOBase implements Dao<T> {

  static {
    ObjectifyService.register(Person.class);
  }
  
  private final Class<T> clazz;
  
  public AbstractDao(Class<T> clazz) {
    this.clazz = clazz;
  }
  
  @Override
  public T find(Long id) {
    return ObjectifyService.begin().find(clazz, id);
  }
  
  @Override
  public T find(String id) {
    return ObjectifyService.begin().find(clazz, id);
  }
  
  @Override
  public T find(Key<T> key) {
    return ObjectifyService.begin().find(key);
  }
  
  @Override
  public Key<T> key(T t) {
    return ObjectifyService.factory().getKey(t);
  }
  
  @Override
  public List<T> findAll() {
    return ObjectifyService.begin().query(clazz).list();
  }
  
  @Override
  public List<T> findAll(List<Key<T>> keys) {
    if (keys == null) {
      return null;
    }
    final Map<Key<T>, T> map = ObjectifyService.begin().get(keys);
    final List<T> list = new ArrayList<T>();
    for (final T t : map.values()) {
      list.add(t);
    }
    return list;
  }
  
  @Override
  public List<Key<T>> key(List<T> list) {
    if (list == null) {
      return null;
    }
    final List<Key<T>> keys = new ArrayList<Key<T>>(list.size());
    for (final T t : list) {
      final Key<T> key = ObjectifyService.factory().getKey(t);
      keys.add(key);
    }
    return keys;
  }
  
  @Override
  public T persist(T t) {
    ObjectifyService.begin().put(t);
    return t;
  }
  
  @Override
  public void delete(T t) {
    ObjectifyService.begin().delete(t);
  }
}

And the actual Person DAO implementation:

package com.exposure101.angular.demo.server.dao;

import com.exposure101.angular.demo.server.model.Person;

public class PersonDao extends AbstractDao<Person> {

  public PersonDao() {
    super(Person.class);
  }
}

And here's the Provider classes:
The Abstract Provider
package com.exposure101.angular.demo.server.services;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

import com.exposure101.angular.demo.server.dao.Dao;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;


public abstract class AbstractProvider<T> {

  @Inject
  private Dao<T> dao;
  
  private final Class<T> clazz;
  
  public AbstractProvider(Class<T> clazz) {
    this.clazz = clazz;
  }
  
  @GET
  @Path("/get/{id}")
  public String get(@PathParam("id") String id) {
    final Gson gson = new GsonBuilder().setPrettyPrinting().create();
    if ("all".equals(id)) {
      return gson.toJson(dao.findAll().toArray());
    } else {
      return gson.toJson(dao.find(Long.valueOf(id)));
    }
  }
  
  @POST
  @Path("/persist")
  public void persist(String body) {
    final Gson gson = new GsonBuilder().setPrettyPrinting().create();
    final T t = gson.fromJson(body, clazz);
    dao.persist(t);
  }
}


And the Person Provider Implementation

package com.exposure101.angular.demo.server.services;

import javax.ws.rs.Path;

import com.exposure101.angular.demo.server.model.Person;

@Path("/person")
public class PersonProvider extends AbstractProvider<Person> {

  public PersonProvider() {
    super(Person.class);
  }
}

Now that this is all up we can test the Web Services.  You should be able to start your server in debug mode and put a breakpoint on the first line in the GET method of your AbstractProvider class.  Once the server comes up try hitting the following link (assuming you didn't change the default port of 8888): http://127.0.0.1:8888/rest/person/get/all.

Time to start the Angular side.


Create a new Angular project using the following commands:

npm install generator-angular generator-karma
yo angular
bower install angular-ui
npm install grunt-proxy

The first thing you'll need to do once you'e finished creating the project is to setup a proxy in your Gruntfile.  By default your Angular Server will run on port 9000 - if you try to access an App Engine Web Service on port 8888 you will get a cross domain error.  In the root of your Angular App open up the Gruntfile.js and edit the following:

  • Before the grunt.initConfig call load the grunt proxy server
  • Create the proxy on an arbitrary port (not 8888 or 9000) and setup the routes as shown below
  • Edit the open task to load your Angular app on the correct port


  grunt.loadNpmTasks('grunt-proxy');
  grunt.initConfig({
    yeoman: yeomanConfig,
    watch: {
      ...
    },
    proxy: {
      proxy1: {
        options: {
          port : 8050,
          host : 'localhost',
          router : {
            'localhost/rest/*' : 'localhost:8888',
            'localhost' : 'localhost:9000'
          }
        }
      }
    },
    connect: {
       ...
    },
    open: {
      server: {
        url: 'http://localhost:<%= proxy.proxy1.options.port %>'
      }
    },
    ...
  });    


From now on, when you start your Grunt server, instead of typing in "grunt server" you will need to call "grunt proxy server".

The first thing I did was build up the views since that's an easy place to start. Under your app's views directory create a file called "CreatePerson.html".  Once you copy in the HTML below it will look as follows:



<div class="hero-unit">
  <div class="row">
    <div class="span2">First Name:</div>
    <div class="span3">
      <input type="text" ng-model="person.firstName"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">Last Name:</div>
    <div class="span3">
      <input type="text" ng-model="person.lastName"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">SSN:</div>
    <div class="span3">
      <input type="text" ng-model="person.ssn"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">Gender:</div>
    <div class="span3">
      <input type="text" ng-model="person.gender"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">Date of Birth:</div>
    <div class="span3">
      <input type="text" ng-model="person.dateOfBirth"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">
      <button class="btn" ng-click="doSubmit()">Submit</button>
    </div>
  </div>
</div>


There's some important things to cover before continuing.  Currently there is no Controller for this View, that doesn't matter right now but it will.  In Angular, each View is mapped to a Controller and the two communicate through a Scope object.  There's some nice visualizations for this on Angular's site but I found it made a lot more sense to me after just implementing it.  It's important to note (and I'll cover it in more detail soon) that everything in Angular is injected - you don't have to worry about instantiating these Controllers or Scope objects.

You'll notice in the above HTML there's a bunch of attributes that start with "ng".  These are Angular's built in directives.  You can think of directives as HTML tasks - they encapsulate both HTML and JavaScript in order to do a certain task.  Let's say in your HTML code you want to turn an input into a lookup widget such that when you begin typing into it, it queries a server and creates a typeahead to display the results.  You can write all the necessary JavaScript and HTML in a directive called "lookupWidget" and simply create your input in HTML along the lines of
<input type="text" data-lookup-widget="lookupResults"/>.

In this file particularly you'll notice the "ng-model" directives.  There is no Controller currently but this directive is actually binding the fields of the "person" object in the Scope to the text value of the inputs.  If that doesn't quite click yet I'm sure it will a few paragraphs down.  It is really important to note that you do not have to explicitly define any of your objects in your Controller, any object used/defined in the View will be present in the Scope.

Now I'm going to setup the actual Controller for this View.

In your app/script/controllers directory, create a file called CreatePersonController.js.  (Whenever you create a new JavaScript file remember to import it in your index.html file)  All this class is going to be responsible for is taking the person object from the view and persisting it to the server.  The code to do this is shown below:

angular.module('angular.demoApp')
  .controller('CreatePersonController', function ($scope, $routeParams, PersonService) {
    'use strict';

    $scope.doSubmit = function() {
        PersonService.persist($scope.person);
    };
});


The syntax on this is a little funky at first but I'll try and break it down.  The first 2 lines are basically "constructing" (not really, more like getting as far as I understand) the Controller and injecting the $scope and $routeParams objects directly from Angular.  As far as I understand, anything injected into the Controller's constructor function starting with a '$' is injected by Angular.  You're also injecting a PersonService object which is not prefaced with a '$' which means it was created by the developer. (This doesn't exist yet but you're going to create it here in a second).  Since everything is injected, any time you find yourself needing an Angular resource you can simply pass it in the constructor function and the Controller will have access to it magically.

You can see some binding from the View to the Controller here as well.  The "$scope.doSubmit()" function will be called on click from the View's submit button, and the person that we built up in the View is accessed in the doSubmit function as $scope.person.  The Controller and View share data through the Scope object, the default scope for the Controller is passed in as $scope.  I'm sure I'll find a better way to word this tomorrow and change up the article.

The last thing to do here is to create the actual PersonService object.  This will be responsible for actually making the Web Service call.  In your app/scripts/services directory create a file called PersonService.js.

angular.module('PersonServices', ['ngResource']).
  factory('PersonService', function($resource) {
    'use strict';

    /* the actual person json object */
    var person = {};

    /* the actual service to hit the server */
    var personService = {};

    var getResource = $resource('/rest/person/get/:id', {id:'@id'}, {
      get: {method:'GET'},
      query: {method:'GET', isArray:true}
    });

    var postResource = $resource('/rest/person/persist', {}, {
      persist: {method:'POST'}
    });

    /* retrieve the medical details from the server for the medical details htid */
    personService.getPerson = function(id) {
      person = getResource.get({id:id});
      return person;
    };

    personService.getPersonArray = function() {
      person = getResource.query({id:'all'});
      return person;
    };

    personService.persist = function(person) {
      postResource.persist(person);
    };

    return personService;
});


This is the actual PersonService Factory.  Angular creates Factories one time only, from now on anything needing access to the Person web services can inject this and access them.  This service object makes use of something in Angular known as a promise - I don't really have anything in the Java world to compare this too but you can kind of think of this similar to a Proxy object (but not really).  When your Controller calls the getPerson() or getPersonArray() methods, a promise object is returned while the resource goes off and makes the call to the server asynchronously.  Once the call finishes, it magically populates the promise object with the data from the server.  So, in your View classes you can use the promise object in each of your fields - when the call to the server starts they will all be blank, when the call to the server is finished your View will populate automatically.

As an implementation detail, in the 'PersonServices' Module constructor you have to pass in the 'ngResource' string as a parameter - this is absolutely mandatory for Angular's $resource object to work.  I think of this similar to a java import.

The last thing you have to do is edit the app.js file to include the CreatePersonView and CreatePersonController.

angular.module('angular.demoApp', ['PersonServices'])
  .config(function ($routeProvider) {

    'use strict';
    $routeProvider
      .when('/create', {
        templateUrl: 'views/CreatePerson.html',
        controller: 'CreatePersonController'
      })
});


Now you can create a new Person in the Create View:



And verify that they were created using the Web Service in a browser.




For anybody that's interested in the read section of this, the HTML and Controller code is below.

<div class="hero-unit">
  <div class="row">
    <div class="span2">First Name:</div>
    <div class="span3">{{person.firstName}}</div>
  </div>
  <div class="row">
    <div class="span2">Last Name:</div>
    <div class="span3">{{person.lastName}}</div>
  </div>
  <div class="row">
    <div class="span2">SSN:</div>
    <div class="span3">{{person.ssn}}</div>
  </div>
  <div class="row">
    <div class="span2">Gender:</div>
    <div class="span3">{{person.gender}}</div>
  </div>
  <div class="row">
    <div class="span2">Date of Birth:</div>
    <div class="span3">{{person.dateOfBirth}}</div>
  </div>
</div>

angular.module('angular.demoApp')
  .controller('ViewPersonController', function ($scope, $routeParams, PersonService) {
    'use strict';

    var id = $routeParams.id;
    $scope.person = PersonService.getPerson(id);
});


And the last thing you'd have to do is add the following lines to the app.js file
      .when('/person/:id', {
        templateUrl: 'views/ViewPerson.html',
        controller: 'ViewPersonController'
      })

And this will be your end result: