Feed aggregator

AngularJS And Browserify — The Dream Team For Single Page Applications

codecentric Blog - Sun, 17-Aug-14 23:45

Browserify Your AngularJS App

Let’s face it, structuring a large JavaScript project is not trivial. The tool Browserify enables you to use CommonJS modules for your frontend JavaScript, even though browsers do not support CommonJS modules natively. Browserify transforms all modules and their dependencies and creates one big lump of JavaScript which works in the browser – the Browserify bundle. Can we also use Browserify in an AngularJS project? Does it even make sense? Yes, and yes, definitely. This post shows how that is supposed to work.

Browserify — Why?

The biggest advantage when using Browserify is the modularization of the code base. Up to and including version ECMAScript 5, JavaScript does not offer a native module concept. Thus, Browserify/CommonJS is a direct competitor to AMD/RequireJS and also to ECMAScript 6 transpilers like Traceur (ES6 offers native modules). In my opinion, Browserify offers a number of advantages.

Including packages from npm directly is one of these advantages, maybe the most important one. The npm registry contains a multitude of JavaScript modules (close to 90,000 modules at the time of writing). And npm is no longer a package manager for server side JavaScript only. The npm registry also contains a lot of packages for the browser. Classics like jQuery, jQuery UI and es5-shim are already available via npm. Each day, more frontend projects make their releases available on npm, so the numbers are growing. Even packages which were not intended to be used in the browser originally can often be used in frontend projects, thanks to Browserify.

Thus, Browserify is a very good alternative to other frontend project setups (like RequireJS or a bunch of script tags).

Browserify — How?

To transform the sources, Browserify starts at one CommonJS module (the entry point) and follows all require statements in this module. The entry point and all dependencies are transferred into the bundle. The require statements in the dependencies are also resolved and included into the bundle. This process is continued recursively until all require statements have been processed and the bundle is complete.

AngularJS and Browserify

Using Browserify means using CommonJS modules and the CommonJS constructs exports/module.exports and require. When you are working with AngularJS at the same time, we need to decide how to to combine CommonJS modules with AngularJS’ dependency system. There are a few proposals how to do this. The most interesting in this respect is probably a presentation by Ben Clinkinbeard from ng-conf 2014. Some of his ideas have been used in this post.

Code

The example repository for this post is available on GitHub. If you would rather read code instead of prose, head over there. The example app is a todo app, as usual. The example repository can also be used as a project template for your next AngularJS project with Browserify.

Include AngularJS

Before we start out implementing the app, we first need to import the AngularJS libraries. Angular Core is available as a package in the npm registry and is maintained by Ben Clinkinbeard. So it should be sufficient to run

npm install angular --save

Then, just use the following in the code:

var angular = require('angular');

That works fine, but as soon as we wanted to include additional AngularJS modules (like angular-route or angular-animate), we might find that just using the npm packages is suboptimal. Both angular-route as well as angular-animate are available on npm, but the packages are all maintained by different people. While Ben’s AngularJS core package has nearly all stable versions from the 1.2 branch (up to 1.2.21, missing only 1.2.22, which is the latest 1.2 release at the time of writing), the other packages are sometimes on older AngularJS versions and it is not even always immediately obvious, to which AngularJS versions a package on npm relates. The relatively new npm package angular-bsfy could be an alternative, as it contains matching versions of all official AngularJS libraries. But its promise to be “always up to date” is already broken with 1.2.17 as the latest version, so it is 2 month and 5 patch levels behind.

All of this might be a bit confusing, at the least, it is annoying. It will probably only change for the better when (if) the AngularJS team decides to publish releases via npm. They actually have been planning to do this for a long time, as can be seen in this lengthy GitHub issue discussion, but it does not seem to have a high priority.

Fortunately, with Browserify it is no big deal to include AngularJS on our own. We just download the required files (angular.js, angular.min.js as well as all additional modules we need, say angular-route.js and angular-route.min.js) in the desired version and put them into a dedicated directory (like app/js/third-party/angular/). Then, we add the following file to that directory:

app/js/third-party/angular/angular-index.js:

require('./angular.min.js');
module.exports = angular;

This is equivalent to the code in Ben Clinkinbeard’s package. We can always change the require statement to require('./angular.js'); to switch to the non-minified version of angular during development.

We also add the following to package.json:

"browser": {
  "angular": "./app/js/third-party/angular/angular-index.js",
  "angular-route": "./app/js/third-party/angular/angular-route.min.js"
}

Now angular core as well as angular-route are available. The advantage of doing this on our own is that we have full control over the versions being used and we do not rely on when and if someone publishes the desired version on npm.

Integrating AngularJS With CommonJS — A Play In Three Acts

In an ordinary AngularJS project (without Browserify) each file usually contains one AngularJS entity, be it a controller, a service, a provider, etc. The typical AngularJS boilerplate code to start the declaration of a controller might look like this:

app/js/controller/todo.js:

(function() {
  'use strict';
   angular
  .module('todoApp')
  .controller('TodoCtrl', function($scope, TodoService) {
    ...
  });
})();

In the simplest possible project setup, all JavaScript files are referenced in the HTML by a bunch of script tags.

app/index.html:

...
<script src="/app/js/service/todos.js" type="text/javascript"></script>
<script src="/app/js/service/imprint.js" type="text/javascript"></script>
<script src="/app/js/controller/edit_todo.js" type="text/javascript"></script>
<script src="/app/js/controller/todo.js" type="text/javascript"></script>
<script src="/app/js/controller/todo_list.js" type="text/javascript"></script>
<script src="/app/js/controller/imprint.js" type="text/javascript"></script>
<script src="/app/js/controller/footer.js" type="text/javascript"></script>
// more script tags
...

The Naive Approach

We could do something quite similar when using Browserify. A CommonJS module to define a controller would look like that:

app/js/controller/todo.js:

'use strict';
var angular = require('angular');
 
angular
.module('todoApp')
.controller('TodoCtrl', function($scope, TodoService) {
  ...
});

The only difference then is that we omit the IIFE — by definition, CommonJS modules always have their own scope and do not access the global scope. This is also why we need the statement var angular = require('angular');, there is no global variable angular.

Since Browserify merges all CommonJS module into a single file, the script tags in index.html will be replaced by a single script tag for the Browserify bundle. This lengthy enumeration now moves to a JavaScript file, for example to our entry point. The file that declares the AngularJS module is a reasonable choice for the entry point.

app/js/app.js:

'use strict';
 
var angular = require('angular');
var app = angular.module('todoApp', [ 'ngRoute' ]);
 
require('./service/todos');
require('./service/imprint');
require('./controller/edit_todo');
require('./controller/todo');
require('./controller/todo_list');
require('./controller/imprint');
require('./controller/footer');
// ... more require statements, one per file

Right now using Browserify does not yet really help to structure the code. We ought to do better than that.

One index.js Per Source Directory

The first flaw we would like to get rid of is the long list of require statements in app.js. app.js will only list the directories from which we import modules.

app/js/app.js:

'use strict';
 
var angular = require('angular');
var app = angular.module('todoApp', []);
 
// one require statement per sub directory instead of one per file
require('./service');
require('./controller');

If we pass a directory instead of a file as the argument to the require function, it will automatically look for a file named index.js in that directory and require this file. To make use of this feature, we create an index.js in each directory that defines which files from this directory will be required:

app/js/controller/index.js:

'use strict';
 
require('./edit_todo');
require('./footer');
require('./todo');
require('./todo_list');
require('./imprint');

A little aside: Reasonable directory structures for AngularJS projects have been discussed a number of times. The suggestions of this post are independent of the directory structure, it does not matter if the directories are created along a technical axis (controller, service, directive, …) or along the business domain.

With this change the code already has become a bit cleaner. But we are still using Browserify primarily as an oversized tool for script concatenation — not entirely satisfying.

Where To Put The AngularJS Boilerplate Code?

Our next optimization takes care of the usual AngularJS boilerplate code that is needed to define an AngularJS entity (controllers, services, …). Instead of putting this code in each CommonJS module, we can also put this into the index.js files:

app/js/controller/index.js:

'use strict';
var app = require('angular').module('todoApp');
 
app.controller('EditTodoCtrl', require('./edit_todo'));
app.controller('FooterCtrl', require('./footer'));
app.controller('TodoCtrl', require('./todo'));
app.controller('TodoListCtrl', require('./todo_list'));
app.controller('ImprintCtrl', require('./imprint'));

Now the individual CommonJS modules for each controller and service are independent of AngularJS:

app/js/controller/todo.js:

'use strict';
 
module.exports = function($scope, TodoService) {
  ...
};

All in all the code is now considerably cleaner. An added bonus of this approach is improved testability for the AngularJS entities (see next section).

Unit Tests

The fact that the individual CommonJS modules are now comprised only of one single function which does not depend on AngularJS, has a distinct advantage: We can write unit tests independently of AngularJS, so we can use any test framework we like. Here is an example with Mocha and Chai:

test/unit/service/todos.js:

'use strict';
 
var chai = require('chai')
  , expect = chai.expect;
 
var TodoServiceModule = require('../../../app/js/service/todos.js');
 
describe('The TodoService', function() {
  var TodoService;
 
  beforeEach(function() {
    TodoService = new TodoServiceModule();
  });
 
  it('should have some todos initially', function() {
    var todos = TodoService.getTodos();
    expect(todos.length).to.equal(4);
    expect(todos[0].title).to.equal('Buy milk');
  });
});

These tests can be executed without AngularJS and, in particular, without a browser. For example, we can simply execute them with Mocha in Node.js. This allows for rapid feedback and can easily be integrated into CI builds (because it is headless).

Additionally we should execute the unit tests in real browsers once in while, since there can be minute differences between Node.js and browser JavaScript runtimes. That’s very easy with Mocha, we just need to put the Mocha library and the tests in a small HTML file. Since the tests contain require statements, we need to process them with Browserify first (see below).

Karma is another possibility to execute tests in the browser. Karma also supports Mocha test suites and it is possible to trigger Karma from a task runner like Gulp or Grunt, so this can be used in a CI environment.

Command Line Tools: Browserify & Watchify

No matter how nicely structured our code is, it’s all for naught when it does not work in the browser. We need to process our sources with Browserify to turn a bunch of CommonJS modules into something that the browser understands:

browserify --entry app/js/app.js --outfile app/dist/app.js

It would be quite annoying for the development workflow if we had to run this manually after every change. That’s the reason for Watchify. Watchify is a companion tool to Browserify and keeps running in the background, watching the source files. As soon as one changes, Watchify recreates the Browserify bundle. It supports the same command line parameters as Browserify, thus the command to start it is:

watchify --entry app/js/app.js --outfile app/dist/app.js.

To browserify the tests (to execute them with Karma, for example) the following command can be used:

browserify test/unit/controller/*.js test/unit/service/*.js --outfile test/browserified/browserified_tests.js

or

watchify test/unit/controller/*.js test/unit/service/*.js --outfile test/browserified/browserified_tests.js

There’s a directory named bin in the example repository, which has some useful shell scripts to start Browserify and Watchify.

Gulp Build, Live Reload, Karma, Protractor And All That

Doing things with a build system might be even more convenient then the command line tools Browserify and Watchify. gulp.js is a good choice here. The example repository contains a gulpfile.js with all the stuff that you would expect from a decent build configuration:

  • linting the JavaScript code with ESlint
  • run unit tests with Mocha (in Node.js),
  • process the code with Browserify (to create a non-minified bunde),
  • ngmin & uglify (to create a minified Browserify bundle),
  • bundle the unit tests with Browserify and execute them in the Browser via Karma,
  • execute end-to-end tests with Protractor,
  • a server for static assets (gulp-connect),
  • browser live reload.

During development we can simply have gulp watch running in the background all the time. Every time we change a source file, the Browserify bundle will be updated and the browser automatically loads the new bundle so that the change is there immediately.

Conclusion

AngularJS projects profit from Browserify, too. It offers some nifty advantages:

  • modularizing with CommonJS
  • simple use of npm packages
  • improved testability, especially for unit testing
  • very mature tooling (Browserify command line tool, Watchify, integration with Gulp and Grunt)

And here we haven’t even tapped the advanced features of Browserify:

  • transforms
  • using Node.js core modules in the browser
  • source maps
  • cloud based cross browser tests with testling-ci

Therefore, the recommendation for today is: Try Browserify in your next project! And then, never again go without it. :-)

This is a translation of a German article published first at AngularJS.de, the leading German site for everything related to AngularJS.

The post AngularJS And Browserify — The Dream Team For Single Page Applications appeared first on codecentric Blog.

Categories: Agile, Java, TDD & BDD

Refcard Expansion Pack: Getting Started with Play Framework

Javalobby Syndicated Feed - Sun, 17-Aug-14 23:30
This week, DZone released its latest Refcard: Preview Text:  This week, DZone released its latest Refcard: Getting Started with Play Framework. If you're interested in learning more about the Play Framework or sharpening your skills, we decided to dig into the DZone archives and find some of the most popular posts we've had on the topic. ...
Categories: Java

Getting Started with MongoDB and Java: Part II

Javalobby Syndicated Feed - Sun, 17-Aug-14 23:00
In the last article, we covered the basics of installing and connecting to MongoDB via a Java application. In this post, I’ll give an introduction to CRUD (Create, Read, Update, Delete) operations using the Java driver. Preview Text:  In this post, I’ll give an introduction to CRUD (Create, Read, Update, Delete) operations using the Java...
Categories: Java

JPA Tutorial: Setting Up JPA in a Java SE Environment

Javalobby Syndicated Feed - Sun, 17-Aug-14 23:00
JPA stands for Java Persistence API, which basically is a specification that describes a way to persist data into a persistent storage, usually a database. We can think of it as something similar to ORM tools like Hibernate, except that it is an official part of the Java EE specification (and it’s also supported on Java SE). Preview Text:  ...
Categories: Java

When is your code DRY enough?

Javalobby Syndicated Feed - Sun, 17-Aug-14 23:00
You're aware that duplication is a rampant disease: more code, more fragile, less maintainable, less readable. You even use one or another tool and make your code reviewed to find most of them. Preview Text:  When facing some duplicate code, you're not always feeling comfortable to dry it up. You're not even sure you'll keep - as is...
Categories: Java

People Are Not Resources

Javalobby Syndicated Feed - Sun, 17-Aug-14 23:00
My manager reviewed the org chart along with the budget. “I need to cut the budget. Which resources can we cut?” “Well, I don’t think we can cut software licenses,” I was reviewing my copy of the budget. “I don’t understand this overhead item here,” I pointed to a particular line item. “No,” he said. “I’m talking about people. Which people can we lay off? We need to cut...
Categories: Java

Unit Testing - Cost vs Benefit

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:30
I have been a big fan of unit testing for a very long time; my blog is ridden with posts about it. Preview Text:  Recently, I came across this Podcast series between Martin Fowler, Kent Beck and DHH which was in response to DHH’s post TDD is dead, long live Testing. Legacy Sponsored:  ...
Categories: Java

Schedule Risk is a Red Herring!!!

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:30
We often hear the term schedule risk, however, it is generally a Red Herring. Preview Text:  Teams use the term 'schedule risk' even when they know a project is late. However, they know that the executives don't know and are not willing to say anything!!! Essentially the team starts hoping for miracles. Legacy ...
Categories: Java

JSON logging in Apache and Nginx with Logentries

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:30
[This article was written by Tom Smit.] Preview Text:  Below we’ll discuss configuring Apache and Nginx both to send JSON formatted logs and how to take advantage of the search functions, sharable dashboards, and reporting capabilities within the Logentries platform. Legacy Sponsored:  ...
Categories: Java

Test-Driven Development (TDD) Shines with Mocking

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:30
The article presents a perspective and some code samples on how one could some cool stuff with Test-driven development (TDD) and Mocking. The code samples are done in Java. Lets briefly understand what is TDD and mocking? Preview Text:  Test-driven development is a software development process in which developers write tests first and, then writing...
Categories: Java

An APM Solution Divided Cannot Stand

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:30
[This article was written by Larry Loeb.] Frustrations with lack of tool unification might just lead to revolution in the APM space… Preview Text:  Application Performance Management (APM) is a broad concept, and many technologies fall under its umbrella. With renewed focus on performance, APM is a very hot topic and a high growth area for...
Categories: Java

Spring Data MongoDB Hello World with Spring MVC: Example

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:30
This article presents detailed steps on what is needed to get started with Spring Data MongoDB while you are working with a Spring MVC web application. The article assumes that you have got the Spring MVC application setup done. Step 1: Create Documents in MongoDB One could download MongoDB from http://www.mongodb.org/downloads page. Once downloaded, do the following to get started. ...
Categories: Java

Mobile Test-Driven Development, Part 3: Running Your Unit Tests From Your IDE

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:30
TDD in Mobile Development – Part 3 Preview Text:  This post shows how we can have test driven development for mobile. We will look at options for running our tests from within our IDE and finding the right test runner for our development env without the need to launch an emulator or deploy to a device every time we want to run the tests. ...
Categories: Java

Graph Theory and Calculating Network Topologies

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:00
 Originally written by Marten Terpstraat the Plexxi blog.  Preview Text:  Any network can be represented as a graph. The switches in the network are the vertices or nodes in the graph, the links between them the edges. Legacy Sponsored:  unsponsored
Categories: Java

From Personas to User Stories

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:00
1 Start with Personas The first step towards writing the right user stories is to understand your target users and customers. After all, user stories want to tell a story about the users using the product. If you don’t know who the users are and what problem we want to solve then it’s impossible to write the right stories and you end up with a long wish list rather than a description of...
Categories: Java

Hello World with ReactJS: Code Example

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:00
This article represents code examples and high level concepts on React.js, a javascript library for building user interfaces, being developed by Facebook Engineers. The concepts shall be presented in detail in upcoming articles. Also, I would have to say that if you are a ReactJS expert and feel that there could be improvements with the code, please drop a line with suggestion and I shall update...
Categories: Java

Coming in ActiveMQ v5.11: In-Memory Scheduler Store

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:00
 Up to this point your only option for doing scheduled message delivery in ActiveMQ required that you start a broker with persistence enabled.  Well, that's not entirely true, if you wanted to apply some configuration magic and start a broker in non-persistent mode and add an instantiated version of the KahaDB based JobSchedulerStore you could but that's not really ideal when you...
Categories: Java

Bootstrapping Cloudify on Devstack

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:00
[This article was written by Yoram Weinreb.] Preview Text:  Cloudify 3.0 is a major milestone for Gigaspaces. It tightens our integration with Openstack and steers the product architecture to closely match the OpenStack architecture stack. There are several ways to get started with Cloudify 3.0 on OpenStack. Legacy ...
Categories: Java

BackBone Tutorial - Part 4: CRUD Operations on BackboneJs Models using HTTP REST Service

Javalobby Syndicated Feed - Sun, 17-Aug-14 22:00
In this article we will discuss how we can perform CRUD operations on a backbone model using a REST based HTTP service. Background Earlier we have discussed about the benefits of using backbone.js and we also looked at the backbone models. Link to complete series: Preview Text:  In this article we will discuss how we can perform CRUD operations on...
Categories: Java

The Best of the Week (Aug. 8): NoSQL Zone

Javalobby Syndicated Feed - Sat, 16-Aug-14 23:00
Make sure you didn't miss anything with this list of the Best of the Week in the NoSQL Zone (August 8 to 14). Here they are, in order of popularity: Preview Text:  Make sure you didn't miss anything with this list of the Best of the Week in the NoSQL Zone. This week's best include six rules of MongoDB schema design, a collection of...
Categories: Java

Thread Slivers eBook at Amazon

Syndicate content