Beliebte Suchanfragen
//

SPA example application with AngularJS and requireJS as a comparison to the ROCA Spring variant

18.3.2013 | 9 minutes of reading time

About three weeks ago, Tobias Flohre published his blog post about a real ROCA application . He has shown that a ROCA application can be developed with ease by utilising tools that have been available (and stable) for a while now. Additionally, the Single-Page Application (SPA) architecture was questioned. With this blog post, I wish to address these questions.

At codecentric we have had quite a few discussions about both, the ROCA and SPA approach. While trying to identify a superior front-end architecture, we came to the conclusion that no architectural style is inherently superior, but that both have strengths and weaknesses. Decisions regarding the architecture therefore need to be made on a case-by-case basis. Nevertheless, we wanted to compare both styles using a small application. This application is the movie database, i.e. the application which was introduced in Tobias Flohre’s article.

When talking about SPAs I am referring to applications which are running in a browser. I further differentiate between architectures that hide the web’s concepts from the developer and the ones which actually utilise HTML, CSS, JavaScript and HTTP. While Vaadin and others fall into the former category, this blog post covers the latter, i.e. architectures which appreciate the web’s concepts, flexibility and even its shortcomings. Plainly said: we write JavaScript!

The following sections are giving examples based on, and are referring to, the SPA version of this application. I deployed the application to Heroku so that you can give it a spin . In case you are interested in the source code: it is available through GitHub. I also deployed the ROCA variant to Heroku for those of you who are interested in a side-by-side comparison.

Note:

Heroku is shutting down both applications from time to time to save resources. Initial loading times may therefore be slow.

Organisation of Code

One of the points questioned was the maintainability of SPA applications.

How do you maintain a large code base? In a language like Java, Python and many more you have the concept of namespaces, i.e. a named scope. Namespaces generally allow you to hide information, analyse dependencies and much more. As you may know, JavaScript does not yet have such a concept, but with the advent of ECMAScript.next, this is going to change . Of course, from the time of arrival of this feature to its actual implementation and widespread support, we will have to to wait a few more years.

But, there is something that you can do today to organise your code: make use of the module pattern! With help of the module pattern (and its variations) you can create a concept similar to namespaces. Addy Osmani wrote a good book about the variations, strengths and weaknesses of this pattern that I urge you to read if you want to learn more. For the sake of simplicity though, I will not go into detail, but instead just show you how I made use of this pattern for the demo application.

1define(["app", "config"], function(app, config) {
2  var module = {};
3 
4  // initialise the module
5 
6  return module;
7});

Let us dissect this piece of code. The define function is immediately called and, as the name implies, defines a new module. The module itself is the result of the function(app, config){} function call. In this case, the defined module would be an object without any properties. define is expecting module dependencies as the first parameter. Dependencies are passed to a module’s constructor in the order in which they are defined.

This can be one of many modules that are asynchronously defined. In order to identify modules, modules need to define an ID like app or config. In this example, the ID is inferred from the name of the file in which the module is defined.

The concept is called Asynchronous Module Definition (AMD) and one commonly used implementation is requireJS . The SPA demo application also uses requireJS.

Maintainable Code

Structuring your code with the help of modules is great, but will not keep you from writing unmaintainable code. A typical candidate for spaghetti code and a violation of the single responsibility principle is the view, i.e. the part of your application which is actually responsible for generating HTML. It is all too easy to litter your controllers (think MVC controllers) with information about the layout, e.g. CSS classes, or to have functionality in your views which should better be placed in your controllers or some other layer / service. Let me show you what a typical view in an AngularJS application looks like (the SPA version of the movie database uses AngularJS).

AngularJS originality implemented the MVC pattern but is nowadays closer to the MVVM pattern. This being said, the view is actually just HTML with AngularJS specific tags and attributes. The following listing shows the view code for adding and editing a movie (without layout related tags and attributes).

1<form>
2  <label for="inputTitle">Title</label>
3  <input type="text" id="inputTitle" autofocus ng-model="movie.title">
4 
5  <label for="inputReleaseDate">Release Date</label>
6  <input type="date" id="inputReleaseDate" ng-model="movie.startDate">
7 
8  <label for="inputDescription">Description</label>
9  <textarea id="inputDescription" ng-model="movie.description">
10  </textarea>
11 
12  <button type="submit" ng-click="save()">Save</button>
13  Cancel
14</form>

Except for a few AngularJS specific attributes which enable data binding, we are using standard HTML. AngularJS is analysing the DOM in search for specific attributes and directives which are then interpreted and bound to the view model. As you can see from the listing, the view model defines the two properties movie and save which are bound to the input elements and click event respectively. The following listing shows AngularJS’ concept of controllers and the definition of both properties.

1function AddMovieCtrl($scope, $location, MovieService) {
2  $scope.movie = {};
3 
4  $scope.save = function() {
5    MovieService.add($scope.movie, function(error, data) {
6      // ...
7    });
8  };
9}

Unlike other frameworks, AngularJS does not require your controllers to inherit from framework specific classes or to define your domain objects in some specific way. Instead, you can use plain old JavaScript functions and objects. The previous listing shows exactly this, a definition of a controller called AddMovieCtrl. The controller function’s parameters are actually its dependencies. The dependencies are resolved by AngularJS and “injected” into the controller. In this specific case, the controller depends on a new scope (the view model), the AngularJS location service (which can be used for redirecting a user) and a user defined service for persistence. User defined services can be easily identified by the absence of the $-character prefix and must be defined beforehand, i.e. AngularJS will throw an exception when the MovieService has not been defined (which stands in contrast to the behaviour of AMD loaders).

The controller function’s body is remarkably boring. The movie property is defined as an empty object and the save function is added to the view model. What I find fascinating is that there is very little glue code necessary to set up data binding or event listeners. AngularJS even includes reasonable default implementations for form submission handlers and event propagation that you are often missing in other frameworks.

Note:

The controllers are actually the reason why AngularJS is not really an MVVM framework as nicely described by Google’s AngularJS tech lead Igor Minar.

Testing

Application testing is, to say the least, diverse. Unit, integration, user interface, performance tests and many more test categories exist. Maybe you even want to hit me with a stick telling me that user interface tests are actually a sub-category of integration tests! Let me keep this section short by only pointing you to JavaScript tools / libraries that target many common requirements.

  • Unit tests
    QUnit
    is jQuery’s battle-proven testing framework.
    Jasmine
    is a BDD style testing framework.
    MockHttpRequest
    to mock the XMLHttpRequest object.
  • Integration and UI tests
    Testacular
    Inifinitest style integration tests in all common browsers. Also supports continuous UI tests (without Selenium). Has a custom Jasmine runner that works great with AngularJS based apps – check out the screencast !
  • Static source code analysis
    JSHint
    enforces coding conventions, checks cyclomatic complexity and more.
  • Code coverage
    Istanbul
    adds instrumentation to track statement, branch and function coverage. Testacular supports Instanbul.

Integrating other Applications

ROCA theory tells us that one can embed a ROCA application easily using an iframe or even extract just part of the generated HTML using XPath. This is possible due to the unobtrusive JavaScript and semantic HTML / posh rules. When displaying information is sufficient for your integration use case, then this is a great and an easy way to do it. Another argument for ROCA is the ability to link to resources, even from other systems. Applications can therefore be integrated through the use of links. But wait – nowadays you can also provide links to specific pages in a SPA. You can try it with the two demo applications by changing the host name part of the URL. Both of them are using the same data source and URL patterns, links are therefore interchangeable! This is possible with the help of the HTML5 history API .

For more elaborate use cases, you probably want to integrate your application on an information level, i.e. receive raw information (maybe XML or JSON) and generate user interface elements to display the information. This way of integrating applications is possible with both architectural styles. ROCA recommends that “Resources have additional representations in other formats, e.g. JSON and/or XML” and thus can support application integration on this level. For SPAs, we do not have a rule, but we need to have some kind of service that the SPA is interacting with in order to retrieve information. Such a service is commonly supporting JSON and could also be consumed by other applications.

Conclusions

This blog post is by no means a detailed comparison of the pros and cons of SPA and ROCA applications. Such an evaluation is currently in progress at codecentric through a student who is writing his thesis. As it stands, one product of this thesis will be a generic decision table that will come in handy when choosing an approach for your front-end. We will keep you posted.

Obviously, the web’s concepts represent an entry barrier. If you have been using something like Vaadin, you have will have to cope with a wider range of technologies and paradigms when implementing ROCA or the presented SPA approach. Additionally, the asynchronous nature of JavaScript combined with promises, different scoping rules and tooling are bound to give you a rough start. Nevertheless, I find that the flexibility which you gain is worth the extra mile.

I would like to close this blog post with a statement of Mirko Novakovic (codecentric board member): “Treat the browser like a device”. In context: In the same way that you develop against APIs of smartphones and tablets, develop for the browser. The gist: do not hide the web under a fat layer of abstractions.

Note:

One thing that I hear people claiming is “that SPAs are not accessible to people with disabilities”. What do you think about accessibility of SPAs? Scared that you cannot use them properly with a screen reader? Do me a favour: install Chromevox , bring up the SPA movie database and give it a spin. You will be pleasantly surprised!

share post

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.