In a sea of client side MVC web frameworks, Angular is becoming the most prominent one due to its increasing popularity. Unlike other MVC frameworks (Backbone, Ember), Angular uses HTML as the templating language and also allow us to build our own HTML directives that can be interpreted by the browser. One of the unique Angular's features are the dependency injection mechanisms which promote code reuse and make it easier to assemble the web application.

In this post we will focus on how to build a simple web application to highlight the most important building blocks every modern Angular application should have. So, let's start.

What we are going to build?

I bet you had already read a lot of tutorials where a todo like application was used to illustrate the basic concepts. I wanted to go step further and build a real time collaborative web application to manage the todo lists. The full source code is hosted on the github. Even simple, the application uses rather complete technology stack.

  • MongoDB, document oriented NoSQL data store.
  • Spring, the Spring MVC framework for building the RESTful web services. Spring Data had been used to simplify data access and persistence through Mongo repositories.
  • SockJS, websockets to make the realtime application.
  • Bourbon/Refills, is the SaSS based framework with very cool components and responsive grids.
  • Bower for dependency management of web artifacts.
rabbit-todo in action

Implementation

Going persistent with Mongo

Spring Data makes it extremely easy to access and manipulate the new generation data access technologies as well as relational databases, providing the well known Spring programming model such as POJOs, templates, etc. For instance, our Todo entity could be modeled as follows.

@Document
public class Todo {

    @Id
    private String id;

    private String name;
    private String description;

    private Date creationDate;
    private List<Task> tasks = new ArrayList<Task>();

}

The @Document annotation identifies the domain object to be saved in Mongo. Spring Data provides the MongoTemplate abstraction we could use to query and manipulate the data store. However, a more consistent and idiomatic approach can be achieved with the data repositories.

public interface TodoRepository extends MongoRepository<Todo, String> {
}

That's the whole code needed to enable the basic CRUD operations on Todo documents. Magic! Isn't it? It is also necessary to activate the repository scanning in the Spring XML configuration file.

<mongo:repositories base-package="org.rabbitstack.rabbit.todo.data.repository" />

Serving up the data via REST

The next step is to expose the HTTP endpoints the will be called from Angular to get todo lists, create new ones, change tasks priority or mark them as done. That won't be a big deal. Spring MVC controllers allow us the create lightweight REST services and map the methods with requests using the @RequestMapping annotation. Let's see some code.

@Controller
@RequestMapping("api/v1")
public class TodoController {

    @Autowired
    private TodoRepository todoRepository;

    /**
     * Gets all todo lists.
     * @return a JSON array of todo lists
     */
    @RequestMapping(value = "/todos", method = RequestMethod.GET)
    public @ResponseBody
    List<Todo> todos() {
        return todoRepository.findAll();
    }
}

That's pretty straightforward. We have annotated the TodoController class with @Controller and @RequestMapping annotations, so the Spring knows this class will act as the controller. Note how the controller receives the reference of the repository and how the findAll method is called to get all created todos in Mongo. When the client (browser) requests the api/v1/todos endpoint the todos method will be triggered to respond with the JSON array of todo lists in the response body.

Pushing with web sockets

Every time a user adds a new todo list, delete the task or performs any other action, we want that changes to propagate to the rest of the users. Instead of pulling for the changes using the traditional XHR calls, the websockets open a bidirectional full duplex channel with the server. Once the connection had been established they can interchange data in real time.

Spring Websocket provides a SockJS endpoint which can be enabled with the following XML configuration.

<websocket:message-broker application-destination-prefix="/todo">
    <websocket:stomp-endpoint path="/io">
        <websocket:sockjs />
    </websocket:stomp-endpoint>
    <websocket:simple-broker prefix="/topic" />
</websocket:message-broker>

This will boot up a broker on /io endpoint so the clients can connect over the Stomp protocol. Here we also tell the Spring to use SockJS websocket emulation. If web browser doesn't support web sockets, SockJS will choose another transport.

On the client side, the Angular service is responsible for establishing the connection with websocket server upon application bootstrap.

var todoApi = {

    stomp : null,

    connect : function(endpoint) {
        var socket = new SockJS(endpoint);
        var defer = $q.defer();
        todoApi.stomp  = Stomp.over(socket);
        todoApi.stomp.connect({}, function(frame) {
            defer.resolve();
        });
        return defer.promise;
    },

    subscribe : function(topic, callback) {
        todoApi.stomp.subscribe(topic, function(data) {
            $rootScope.$apply(function() {
                callback(data.body);
            });
        });
    }

The subscribe function registers a callback that is called every time the content is being pushed from the server. For sending the messages to a subscribed topic, Spring provides the SimpMessagingTemplate template. See the code below. Upon the creation of a todo list convertAndSend will serialize a Todo object to JSON and send it to the browser.

/**
 * Creates a new todo list.
 * @param todo list to be created
 */
@RequestMapping(value = "/todos", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
public void createTodo(@Valid @RequestBody Todo todo) {
    todo.setCreationDate(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()));
    todo.getTasks().forEach(
                        task -> task.setId(UUID.randomUUID().toString())
                    );
    Todo t = todoRepository.insert(todo);
    messagingTemplate.convertAndSend("/topic/todos/created", t);

}

On the Angular side, we inject the TodoService into the controller, and then call the connect method to initiate the connection and register the callbacks that are triggered when message is received from the server.

todoService.connect('/io').then(function() {

    todoService.subscribe('/topic/todos/created', function(todo) {
	$scope.todos.push(angular.fromJson(todo));

});

Consuming the REST API

Angular ships with the built in $http service that can be used to perform requests. For example to create a new todo list, we can call the post method.

create : function(name, description, tasks) {
    $http.post(API_ENDPOINT, { name : name,
                              description : description,
                              tasks: tasks});

}

Similarly, we can send a GET request like this.

list : function() {
   var promise = $http.get(API_ENDPOINT)
        .then(function(res) {

            return res.data;

        });
   return promise;
}

This function returns a special promise object which encapsulates the asynchronous action. In the controller we wait for the operation to complete, and then we provide the scope with the result.

todoService.list().then(function(todos) {
    $scope.todos = todos;

});

Rendering the views

As we already stated, Angular uses the HTML templates to build up the views with the set of well known directives. They provide bidirectional data binding, iterations, conditional rendering, event handlers, etc. Here is a part of the view which renders todo lists.

<div class="flex-boxes">
	<div class="flex-box" ng-repeat="todo in todos">
		 <div class="delete"><i ng-click="deleteTodo(todo.id)" class="icon ion-close-circled"></i></div>
                <h1 class="flex-title uppercase"></h1>

                <p class="todo-description"></p>
    </div>
</div>