Maintainable Rich Web Applications with AngularJS – Part 1

Compared to Thin Web Applications, Rich Web Applications imply a higher grade of client-side functionality and complexity as opposed to the server-side. The user interface of a typical Thin Client architecture is server-generated (per page), while rich clients are complete applications running in a web browser. This allows for user interfaces that are more user-friendly, more performant and offline-enabled. Such characteristics, offline capability in particular, are especially important for mobile applications.

HTML5 has established itself as an alternative to solutions like Flash, Java Applets and Silverlight. Compared to proprietary products it offers an open and platform-independent technology standard. Here, HTML and CSS describe the static structure as well as the design of the user interface, while the client-side dynamics is implemented using new HTML5-APIs and JavaScript.

The development of Rich Web Clients using HTML and JavaScript is complicated by maintainability problems though, as the combination of HTML and JavaScript alone offers no possibility to modularize and test the client in a clean fashion. AngularJS considers itself to be an HTML extension dealing with this problem in order to enable the development of maintainable JavaScript-/HTML based Rich Web Applications.

This blog post series introduces the JavaScript framework AngularJS. This first post explains the essential concepts of AngularJS, like the application of the Model View Controller pattern, the extension of HTML by so-called directives as well as the routing concept. In the second post, we will show the integration of unit and end-to-end tests as well as embedding those into a Maven build process.

Introduction

AngularJS is intended to add dynamics to static HTML. It allows to modularize your JavaScript logic and enables separation of static UI description and dynamic control logic in terms of the MVC paradigm. Compared to libraries such as jQuery, AngularJS does not manipulate the DOM tree imperatively. Imperative manipulation often results in a reduced maintainability. Instead, AngularJS enriches the HTML code with attributes that add dynamics to HTML in form of JavaScript control logic.

Blog Application

The following demo, a blog application, demonstrates how to develop a maintainable and testable rich web application. It is implemented as a single page application.

architecture diagram

The server is a JBoss AS7 providing a REST interface to be used by the AngularJS client. Using this application, the most important aspects of AngularJS are presented. Feel free to check Github for the sources.

AngularJS-Blog

Modules

One criterion for well maintainable applications is a good structure, especially in complex projects. A maintainable structure can be achieved by proper modularization. AngularJS offers its own concept that helps modularizing JavaScript code. The following service module contains all services, which, in the context of the blog application, are responsible for connecting to the server.

angular.module('Services', []).
  /** BlogPostService */
  factory('BlogPostService', [
    '$http',
    function($http) {
      // ...
      return { // ... };
    }
  ]).

  /** CommentService */
  factory('CommentService', [
    '$http',
    function($http) {
      // ...
      return { // ... }
  ]).
// ...

In line 1, the module is initialized by the module() function. This function requires the module name and a list of all depending modules as parameters. Following that, all respective module elements are defined in specific blocks. In the given example, the services are implemented using a factory block. See here for an overview of all possible blocks and how they can be used.

Dependencies to other components are resolved via dependency injection, a concept to configure dependencies of a component at runtime, increasing testability and reusability.

Model View Controller

The Model View Controller pattern structures user interfaces into three parts: The model contains the data which is to presented by the view, while the controller acts as a mediator between user input and model.

To enable communication between model, view and controller, AngularJS uses so-called scopes: Namespaces that are implemented as simple JavaScript objects. Every AngularJS application has exactly one root scope object but may have several child scopes. For example, every controller has its own scope that is not globally visible. The following example shows the BlogPostController responsible for the detail view of a blog post.

// ...
  controller('BlogPostController', [
    '$scope',
    'BlogPostService',
    'CommentService',

    function($scope, BlogPostService, CommentService) {
      $scope.blogPost = BlogPostService.blogPost;
      $scope.commentService = CommentService;
    }
  ]).
// ...

In line 9, the CommentService is added as a property to the scope object of the controller. This service includes, among other things, the data model of the blog post comments. As all scope objects are monitored by AngularJS, changes of a data model are registered immediately. In this case, a change of the data model of the comments updates the assigned view. Thus, after a user write a new blog comment, it immediately becomes visible in the comment list.

MVC diagram

In the context of AngularJS, data models are always properties of a scope object and only visible in the particular view that is assigned to the controller.
AngularJS supports two-way data binding between view and controller. In contrast most template systems only offer data binding in one direction, leading to unregistered model changes. While it is of course possible to monitor the user interaction manually, AngularJS can do it automatically. This keeps the controller free of code for view manipulation and therefore it is well testable.

Views / Partials

Views – called partials in AngularJS – are defined in plain HTML and extended with dynamics (JavaScript logic) using so-called directives. For this purpose, AngularJS comes with a predefined set of directives which can be declared as an attribute, class or element name.

Let’s look at the view used to display the blog post list on the blog demo homepage. It shows how to use the directive ng-repeat to easily create a list of all posts.

<div id="blogPostList"
     ng-controller="BlogPostListController">
  <article ng-repeat="post in blogPostService.blogPosts">
    <h2>{{ post.title }}</h2>
    <h5>
      {{ post.author.firstname }} 
      {{ post.author.surname }} - 
      {{ post.created | date:'dd.MM.yyyy H:mm' }}
    </h5>
    <p>{{ post.content | blogPostPreview }}</p>
    <p>
      <a href="#/post/{{ post.id }}">Read more...</a>
    </p>
  </article>
</div>

In line 2, the controller is assigned using the directive ng-controller. With that in place, all objects and functions of the controller scope are visible within the view. The blog post list is generated from line 3 onward. By assigning the directive ng-repeat, AngularJS iterates through the model (blogPosts) and lists all elements according to the defined partial. The object values are printed using the curled braces: angular expressions that are evaluated during runtime. The following code fragment shows a part of the generated blog post list.

<div id="blogPostList"
     ng-controller="BlogPostListController"
     class="ng-scope">
  <!-- ngRepeat: post in blogPostService.blogPosts -->
  <article ng-repeat="post in blogPostService.blogPosts"
           class="ng-scope">
    <h2 class="ng-binding">
      Lorem ipsum
    </h2>
    <h5 class="ng-binding">
      Jerry Francis - 10.01.2013 10:49
    </h5>
    <p class="ng-binding">Lorem ipsum dolor sit amet, consectetur adipisicing elit...</p>
    <p><a href="#/post/17">Read more...</a></p>
  </article>

  <article ng-repeat="post in blogPostService.blogPosts"
           class="ng-scope">
    <h2 class="ng-binding">
      Sed ut perspiciatis
    </h2>
    <h5 class="ng-binding">
      John Doe - 09.01.2013 22:12
    </h5>
    <p class="ng-binding">Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium...</p>
    <p><a href="#/post/18">Read more...</a></p>
  </article>
  <!-- ... -->
</div>

Controller

View and controller communicate via the scope of a controller. All objects and functions that are required by the view have to be added to the scope.

The following example shows a simple controller implementation responsible for handling the blog post list.

// ...
  controller('BlogPostListController', [
    '$scope',
    'BlogPostService',
     
    function($scope, BlogPostService) {
      $scope.blogPostService = BlogPostService;
    }
  ]).
// ...

A controller can be defined in the controller block of a module as follows: First, the controller name is declared (line 2), followed by a list of all depending services and the implementation of the controller as a JavaScript function (line 6).

Services

AngularJS lets you create your own service objects. Furthermore, there are several predefined services, distinguishable by the $ character. A list can be found in the AngularJS API reference.

The following example shows the BlogPostService used to communicate with the REST backend. Also, it includes the model of the blog post list.

// ...
  factory('BlogPostService', [
    '$http',

    function($http) {
      // ...
      return {
        // ...
        /** data model: blog post list */
        blogPosts: [],

        fetchBlogPosts: function() {
          var self = this;
          return  $http.get(restUrl).
            success(function(data) {
              self.blogPosts = data;
              return self.blogPosts;
            }).
            error(function(data) {
              return data;
            });
        },
        // ...
      };
    }
  ]).
// ...

The definition of a service is possible by using a factory block. The structure is equivalent to that of the controller block. First, the service name is declared, followed by a list of the required services and a function returning the service object.

Routing

Single page applications usually do not support the browser history out-of-the-box. Thus, typical functions like navigating via browser control elements (forward and back buttons) as well as bookmarking require specific support. AngularJS offers a routing system to deal with this. The routes are defined within the config block.

// ...
  config([
    '$routeProvider',

    function($routeProvider) {
      $routeProvider.
        when('/', {
          templateUrl: 'partials/blog-post-list.html',
          // ...
        }).
        // ...
        when('/login', {
          templateUrl: 'partials/login-form.html'
        }).
        // ...
    }
  ]).
// ...

A single route is defined via the when() function of the $routeProvider service. The definition consists of the path and several optional parameters, like the templateUrl.
By appending a hash (#) to the URL (also called fragment identifier), AngularJS solves the browser history issue. For the login route, the complete URL looks like this:

url_hash_en

By appending this hash on every route request, a new entry will be added to the browser history, enabling bookmarking as well as navigating via the browser control elements.

Conclusion

By extending HTML with directives and by using two-way data binding, it’s possible to separate static UI description and dynamic application logic. Common DOM manipulation code is not necessary, increasing testability of controller and service components.

Modularizing components using AngularJS modules leads to well structured and maintainable JavaScript applications. Dependencies between modules and to AngularJS services are resolved using dependency injection. The integrated routing concept enables browser history for single page applications.

In the next blog post, we will discuss the integration of unit and end-to-end tests and present a way on how to embed those into a Maven build process.

Feel free to contact us via email for any questions and remarks:

till.hermsen [at] akquinet.de
philipp.kumar [at] akquinet.de

13 thoughts on “Maintainable Rich Web Applications with AngularJS – Part 1

  1. Pingback: JavaPins
  2. The explanation for this is for the reason that of how people today lookup for data on web pages such as Google and Bing.

    Commonly we plug our products into wall outlets which are a legitimate sine wave Ac resource.
    ” You can neglect the initially two listings because they are paid out listings.

  3. Thank for you submitting ;-), it is a complete architecture. But i dont know, how to deploy this application. Yes the every test is ok.
    The deploy war is ok but i can’t view in log the deployment rest service or ejb in my jboss log.
    the every jar is present in WEB-INF/lib. localhost:8080/blog/ is ok but the request down because the rest service is’nt deploy. Sorry but I’m used to deploy ear in such cases. thanks :-)

  4. Hello,

    to deploy the application follow the following steps:
    – run ‘mvn package’
    – copy the WAR ‘angularjs-blog-web/target/angularjs-blog-web-1.0-SNAPSHOT.war’ to your JBoss AS 7 deployments folder
    – run AS 7 standalone script

    Now the application should be deployed successfully. If you still can’t deploy the application, don’t hesitate to contact us.

    Till Hermsen

  5. Helo Til, thank you for your reply

    I made ​​for deployment jboss-as: deploy works very well.

    However I thought it was possible to do hot deployment with eclipse. directly with your architecture maven.
    This is the type of deployment that does not fontionne.

    Otherwise I continue to develop the blog with various features, I think aillor should add a security token services with oauth2 rest (I never did but I’m going to start)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s