Coding Experience

Having fun coding and learning.

Automatic Retry on Exception in Angular

| Comments

Every so often, you run across some action, which just fails, where the best response it to just try it again. This is particularly true when dealing with an external source, like a database or web service, which can have network or other temporary problems, which would have cleared up when you repeat the call seconds later.

Often, these actions fail by throwing an exception which makes the process of trying the call again rather cumbersome. Having to deal with this a number of times in one application, I decided to wrap the full procedure up in an angular service, which I share here with you:

Sleep function

First we need to implement a sleep function, that will just wait for a specified interval of milliseconds while returning a promise.

You would think that the best fit is to use $timeout.
The problem is, it has a bad impact on your unit tests…. The reason is, if you use $timeout, you will have at the end of your test to call $timeout.flush() as many times as $timeout was called in your implementation.

Instead, if we use the standard javascript setTimeout function, we only have to call once $rootScope.$apply() at the end of our test and we’re done.

Let’s start by defining our service with an empty implementation: In addition to sleep, we will expose 2 other functions toAsync and retry, that we’ll explain later.

promiseService.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var app = angular.module("myApp", []);

var dependencies = ['$q', '$rootScope'];
var service = function($q, $rootScope) {

  var sleep = function() {

  };

  var toAsync = function(){
  
  };

  var retry = function() {

  };

  return {
      sleep : sleep,
      toAsync : toAsync,
      retry : retry
  }    

};

app.factory("promiseService", dependencies.concat(service));

For the implementation of sleep we are going to generate our own promise, call setTimeout, and not forget to wrap the callback in a $rootScope.$apply() call.

sleep function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var sleep = function(interval) {
  // check parameter
  if(!(interval === parseFloat(interval)) || interval < 0)
      throw new Error("interval must be a positive float");

    var deferred = $q.defer();

    // sleep
    setTimeout(function () {
      $rootScope.$apply(function () {
          deferred.resolve(interval);
        });
    }, interval);

    return deferred.promise;
};

Before we continue, let’s write some unit tests to verify the implementation. I’m using here jasmine 2.0 which comes with a new syntax for async testing.

We have 2 options here:
First we can use the new async syntax and It’s pretty simple : when testing an async function (aka a promise), you add a done parameter to your it, and you call done() when the promise returns some result. In addition, if your implementation uses setTimeout, you add a call to $rootScope.$apply(). The caveat of this method is that the test will wait for the timeout to callback. So it is more an end to end test than an unit test.

The second method, is to use the clock mockup provided by jasmine, that executes synchronously any code using setTimeout or setInterval.
It goes like this:

  • call jasmine.clock().install() in the beforeEach
  • call jasmine.clock().tick(xxx) in the test passing the time in milliseconds that should have elapsed
  • call jasmine.clock().uninstall() in the afterEach
promiseService.tests.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
describe("Service : promiseService" , function() {
  var service;
  var $rootScope;
  var $httpbackend;
  var $http;

    beforeEach(module("myApp"));

    beforeEach(inject(function ($injector) {
        service = $injector.get("promiseService");
        $timeout = $injector.get("$timeout");
        $rootScope = $injector.get("$rootScope");
        $httpBackend = $injector.get("$httpBackend");
        $http = $injector.get("$http");
    }));

    beforeEach(function(){
        jasmine.clock().install();
    });

    afterEach(function() {
        jasmine.clock().uninstall();
    });

    var tickClock = function() {
        jasmine.clock().tick(50000);
        $rootScope.$apply();
    };

    it("should_be_defined", function(){
        expect(service).toBeDefined();
    });

    it("sleep_with_not_integer_interval_should_throw_exception", function () {
        expect(function () {
            service.sleep("invalid");
        }).toThrow(new Error("interval must be a positive float"));
    });

    it("sleep_with_not_positive_integer_interval_should_throw_exception", function () {
        expect(function () {
            service.sleep(-2);
        }).toThrow(new Error("interval must be a positive float"));
    });

    it("sleep_with_positive_integer_interval_should_succeed", function () {
        var interval = 100;
        service.sleep(interval).then(function (result) {
            expect(result).toEqual(interval);
        });
        tickClock();
    });

});

We have a fully tested sleep function.

toAsync function

The next function that we need to write is a function that will transform a passed function into a promise. That way we can retry on fail, either standard functions, or promises. The trick here, if you want your unit test to pass, is to encapsulate the call to the passed function in a try catch block. It is similar to $q.when, but instead of passing a value or a promise, we pass a function or a promise.

toAsync
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 var toAsync = function (action) {
    if (typeof action !== "function") {
        throw new Error("action must be a function");
    }
    var deferred = $q.defer();
    try {
        var retval = action();
        deferred.resolve(retval);

    }
    catch (ex) {
        deferred.reject(ex);
    }
    return deferred.promise;
};

And the unit tests :

toAsync unit tests
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
it("toAsync_with_invalid_parameter_function_should_throw_exception", function () {
    expect(function () {
        service.toAsync("invalid");
    }).toThrow(new Error("action must be a function"));
});

it("toAsync_with_valid_sync_function_should_succeed", function () {
    spyOn(mockHelper, 'addOne').and.callThrough();
    var action = function () {
        return mockHelper.addOne(100);
    };

    service.toAsync(action).then(function (result) {
        expect(result).toBe(101);
        expect(mockHelper.addOne).toHaveBeenCalledWith(100);

    });
   tickClock();
});

it("toAsync_with_valid_async_function_should_succeed", function () {
    spyOn(mockHelper, 'getUrl').and.callThrough();
    $httpBackend.when('GET', '/dummy').respond(mockHelper.dummyResponse);
    var action = function () {
        return mockHelper.getUrl();
    };
    service.toAsync(action).then(function (result) {
        expect(mockHelper.getUrl).toHaveBeenCalled();
        expect(result.data).toEqual(mockHelper.dummyResponse);

    });

    $httpBackend.flush();
    tickClock();
});

it("toAsync_with_faulty_sync_function_should_succeed", function () {
    spyOn(mockHelper, 'faultyFn').and.callThrough();
    var action = function () {
        return mockHelper.faultyFn();
    };
    service.toAsync(action).then(null, function (rejection) {
        expect(mockHelper.faultyFn).toHaveBeenCalled();
        expect(rejection.message).toBe("I'm a faulty function");

    });
    tickClock();
});

var mockHelper = {
    faultyFn : function () {
        throw new Error("I'm a faulty function");
    },

    addOne : function (value) {
        return value + 1;
    },

    getUrl: function () {
        return $http.get("/dummy");
    },

    dummyResponse : {
        "id" : 1,
        "content" : "Hello World"
    }
};

The mockHelper object holds our unit test functions, so we can reuse and spy on them across the tests. This is usefull for checking how many times they are called and with which parameters.

retry function

This is the last piece of the puzzle. To be as generic as possible, our function will accept a set of parameters in order to control how many times we should retry on fail, the interval between each trial, and also an interval multiplicator if we want to add some extra delay between each trial.

The implementation is straight forward, using the building blocks we previously wrote. In the first part we just do some argument checking, and assign default values. In the second part, we recursivly call resolver, which, execute the passed action, and if an expection is detected, do a sleep and retry.

retry function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var retry = function (action, options) {

    retry.DEFAULT_OPTIONS = {
        maxRetry : 3,
        interval : 500,
        intervalMultiplicator : 1.5
    };

    if (typeof action !== "function") {
        throw new Error("action must be a function");
    }
    if (!options) {
        options = retry.DEFAULT_OPTIONS;
    }
    else {
        for (var k in retry.DEFAULT_OPTIONS) {
            if (retry.DEFAULT_OPTIONS.hasOwnProperty(k) && !(k in options)) {
                options[k] = retry.DEFAULT_OPTIONS[k];
            }
        }
    }

  var resolver = function(remainingTry, interval) {
        var result = toAsync(action);
        if (remainingTry <= 1) {
            return result;
        }
        return result.catch(function (e) {
            return sleep(interval).then(function () {
              // recursion
                return resolver(remainingTry - 1, interval * options.intervalMultiplicator);
            });
        });
    }
    return resolver(options.maxRetry, options.interval);
};

Here are the unit tests:

retry function unit tests
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
it("retry_with_invalid_parameter_function_should_throw_exception", function () {
    expect(function () {
        service.retry("invalid");
    }).toThrow(new Error("action must be a function"));

});

it("retry_with_faulty_sync_function_should_succeed", function () {
    spyOn(mockHelper, 'faultyFn').and.callThrough();
    var action = function () {
        return mockHelper.faultyFn();
    };
    var promise = service.retry(action);
    promise.then(null, function (rejection) {
        expect(mockHelper.faultyFn).toHaveBeenCalled();
        expect(mockHelper.faultyFn.calls.count()).toBe(3);
        expect(rejection.message).toBe("I'm a faulty function");

    });
    tickClock();

});

it("retry_with_faulty_sync_function_and_options_should_succeed", function () {
    spyOn(mockHelper, 'faultyFn').and.callThrough();
    var action = function () {
        return mockHelper.faultyFn();
    };
    var promise = service.retry(action, {maxRetry : 5});
    promise.then(null, function (rejection) {

        expect(mockHelper.faultyFn).toHaveBeenCalled();
        expect(mockHelper.faultyFn.calls.count()).toBe(5);
        expect(rejection.message).toBe("I'm a faulty function");

    });
    tickClock();

});

it("retry_with_valid_sync_function_should_succeed", function () {
    spyOn(mockHelper, 'addOne').and.callThrough();
    var action = function () {
        return mockHelper.addOne(100);
    };
    var promise = service.retry(action);
    promise.then(function (result) {
        expect(result).toBe(101);
        expect(mockHelper.addOne).toHaveBeenCalled();
        expect(mockHelper.addOne.calls.count()).toBe(1);

    });
   tickClock();
});

it("retry_with_valid_async_function_should_succeed", function () {
    spyOn(mockHelper, 'getUrl').and.callThrough();
    $httpBackend.when('GET', '/dummy').respond(mockHelper.dummyResponse);
    var action = function () {
        return mockHelper.getUrl();
    };
    var promise = service.retry(action);
    promise.then(function (result) {
        expect(mockHelper.getUrl).toHaveBeenCalled();
        expect(result.data).toEqual(mockHelper.dummyResponse);

    });
    $httpBackend.flush();
    tickClock();
});

That’s it. Our service is fully unit tested (100% coverage!!!), and we can reuse it anywhere in our applications.

The repository is available here.

Let me know what you think and happy coding.

Avi Haiat

How to Install and Use Octopress on Windows

| Comments

A quick and easy 6 steps tutorial to get up and running with octopress and github pages

NOTE: As a pre-requisite you should make sure you have a public github repo with the name : yourusername.github.io

Step 1: Install Chocolatey

  1. Got to http://chocolatey.org
  2. Open a command prompt
  3. cd to c:\
  4. Paste in and execute the following code from chocolatey home page
Install Chocolatey
1
2
3
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex
((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))"
&& SET PATH=%PATH%;%systemdrive%\chocolatey\bin

This will install chocolatey in C:\Chocolatey

Step 2: Make sure Git is installed or install it via Chocolatey

Install Git with Chocolatey
1
choco install git

To check that git is installed close and reopen a command prompt and run the following command:

Check Git version
1
git --version

You should get git version 1.0.0.msysgit.0

Step 3 : Install Ruby and Python

  1. Install Ruby 1.9.3-p545 from http://rubyinstaller.org
  2. Ruby should be installed in C:\Ruby193
  3. Add the following path to the system variables Path : c:\Ruby193\bin
  4. Exit and reopen command prompt and run ruby --version, you should get ruby 1.9.3-p545
  5. Install ruby developpement kit with chocolatey
Install Ruby Devkit with Chocolatey
1
2
c:
choco install ruby.devkit

This will install the devkit in C:\Devkit
6. Install python

Install Python with Chocolatey
1
choco install python

7. Exit and reopen command prompt and run python --version, you should get Python 2.7.6

Step 4 : Install Octopress

Navigate to your github local repositories folder (for example mine is d:\github) The installation of Octopress consist in cloning the repository of Octopress

Clone Octopress repository
1
git clone git://github.com/imathis/octopress.git blog

Navigate to the created folder and then run the following command to install the dependencies

Install Octopress dependencies
1
2
gem install bundler
bundle install

Then install the default Octopress theme

Install Octopress default theme
1
rake install

Next edit _config.yml and adjust it with your blog properties (title,name, google tracker, etc…)

And now we can generate the site

Generate and preview Octopress blog
1
rake generate && rake preview

You can open a browser and navigate to http://localhost:4000

Step 5 : Blogging

To create a new post

Create a new blog post
1
rake new_post["Hello World Post No 1"]

The new post file is a simple markdown file generated in source\_posts

Step 6 : Publishing your blog to github pages

Github pages expect to have two branches, the source branch and the master branch. The master branch is the one that github pages will show. The changes in the source branch won’t be published until you push your changes to master. You can configure those branchs manually and add your github pages repository as a remote, but there is also a rake task that does all for you:

rake command for setting up github pages
1
rake setup_github_pages[repo].

In the case of this blog, I ran rake setup_github_pages[git@github.com:yourusername/yourusername.github.io.git] and then rake deploy to upload the blog. In a snap the blog will be accessible at the url http://yourusername.github.io.

Remember that rake deploy just generates the blog a push to the master branch. Your source branch won’t be uploaded to github if you don’t want to. You probably want, to have a secure backup online, among other reason. Commit your changes and do

pushing the sources
1
git push origin source.

Workflow when editing your blog

  1. Make your modification to your local repo using rake new_post and editing your markdown
  2. rake generate && rake preview to check your changes
  3. git add . and git commit -m "update blog"
  4. rake deploy to deploy the resulting blog
  5. git push origin source to deploy your Octopress sources

Tips

If you need to put images in your blog just place them in source\images and reference them with

img
1
<img src="{{root-url}}/images/yourimage.png" />

If you get an error from github when executing rake deploy saying that

1
2
3
4
5
6
7
! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/user/user.github
it'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Modify the Rakefile at the root of the project by replacing the following line:

1
system "git push origin #{deploy_branch}"

with

1
system "git push origin +#{deploy_branch}"

Try to deploy again and then revert back Rakefile to its original content

Now you have no excuse not making a blog of your own…

Let me know what you think and happy coding.

Avi Haiat

Extending an Existing Directive in Angularjs

| Comments

Working example : http://thaiat.github.io/angular-extending-directive/

Let’s say you have a third party angularjs directive that you want to extend or simply access the api defined by its controller.
We could use require but that means that we have to put the 2 directives on the same element, or that the extended directive should be contained inside the first one (looks weird), because require will look up the chain of html.

Well… this is not always possible as we do not have control on the code defining the first directive. It could restrict its usage to ‘E’, meaning that our extended directive cannot be anymore retricted to ‘E’.

How can we easily do that, and in a more natural way meaning the extended directive should wrap the first directive ?

Let’s first start with some simple html

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<html>
    <head>
        <title>angular-extending-directive</title>
        <style type="text/css">
       .circle {
            border: 1px black solid;
            text-align: center;
            padding: 4px;
            font-size: 40px;
            border-radius: 50px;
            -moz-border-radius: 50px;
            border-radius: 50%;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            -khtml-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            cursor: pointer;
        }
         .counter {
            width: 50px;
            height: 50px;
            background-color: green;
        }
        </style>
        <script type="text/javascript" src="bower_components/angular/angular.js"></script>
        <script type="text/javascript" src="scripts/app.js"></script>
    </head>
    <body ng-app="myApp">
        <h1>counter</h1>
        <counter></counter>

    </body>
</html>

We are defining a counter directive with some minimal css.

Our counter directive should display a number, starting at 0, and increment it each time we click on it. Most of the chances are that this third party directive will use an isolate scope.

Again this is pretty simple, and here is the code.

scripts/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var myApp = angular.module('myApp', []);
myApp.directive('counter', function () {
  return {
    scope: {},
    controller: function ($scope, $element, $attrs, $transclude) {
      $scope.value = 0;
      $scope.increment = function () {
        $scope.value += 1;

      };
    },

    link: function (scope, iElm, iAttrs, controller) {
      iElm.on('click', function (e) {
        e.stopPropagation();
        scope.$apply(function () {
          console.log('click counter');
          scope.increment();
        });
      });
    },
    restrict: 'E',
    template: '<div class="circle counter"></div>'
  };
});

The directive presents a circle with a number in it. Each time you click it will increment the number.

Now we want to use this existing directive but extend its behavior. For the matter of the explanation, let’s create a new directive called wrappercounter that wraps it into a wider circle, and when will click on it, it should log a console message.

The html for this new directive should be:

index.html
1
2
3
4
<h1>wrapcounter</h1>
<wrapcounter>
  <counter></counter>
</wrapcounter>

and to please our eyes we can add the following css

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
.wrapcounter {
    width: 100px;
    height: 100px;
    background-color: red;
    box-pack: center;
    box-align: center;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    cursor: pointer;
}

Depending on how the first directive counter was written we have 3 ways to achieve this.

1st way : element.isolateScope

If the first directive controller uses $scope (like above) we have to retreive the inner element, and the api declared by the scope will be available through element.isolateScope().

scripts/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
myApp.directive('wrapcounter', function () {
  return {
    restrict: 'E',
    transclude: true,
    template: '<div class="circle wrapcounter" ng-transclude></div>',
    link: function (scope, iElm, iAttrs, controller) {
      // retreive the inner directive element
      var counter = iElm.find('counter')[0];

      var innerScope = angular.element(counter).isolateScope();

      iElm.on('click', function (e) {
        e.stopPropagation();
        scope.$apply(function () {
          // decorating the increment function with a console log.
          console.log('click wrapper');
          // accessing the inner directive api
          innerScope.increment();

        });
      });
    }
  };

});

2nd way : element.controller(name)

This only works if the controller of the first directive uses the this (or controllerAs) syntax. Let’s modify the first directive

scripts/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
myApp.directive('counter', function () {
  return {
    scope: {},
    controller: function ($scope, $element, $attrs, $transclude) {
      var vm = this;
      vm.value = 0;
      vm.increment = function () {
        vm.value += 1;
      };
    },
    controllerAs: 'vm',
    link: function (scope, iElm, iAttrs, controller) {
      iElm.on('click', function (e) {
        e.stopPropagation();
        scope.$apply(function () {
          console.log('click counter');
          scope.vm.increment();
        });
      });
    },
    restrict: 'E',
    template: '<div class="circle counter"></div>'
  };
});

And now the second directive

scripts/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
myApp.directive('wrapcounter', function () {
  return {
    restrict: 'E',
    transclude: true,
    template: '<div class="circle wrapcounter" ng-transclude></div>',
    link: function (scope, iElm, iAttrs, controller) {
      // retreive the inner directive element
      var counter = iElm.find('counter')[0];

      // retreive the inner controller
      var innerController = angular.element(counter).controller('counter');

      iElm.on('click', function (e) {
        e.stopPropagation();
        scope.$apply(function () {
          // decorating the increment function with a console log.
          console.log('click wrapper');
          // accessing the inner directive api
          innerController.increment();

        });
      });
    }
  };

});

3rd way : element.data

Looking at the angluarjs source code we see this piece of code:

angular.js
1
2
3
if (!hasElementTranscludeDirective) {
     $element.data('$' + directive.name + 'Controller', controllerInstance);
}

So we can use this to get access to the controller:

scripts/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
myApp.directive('wrapcounter', function () {
  return {
    restrict: 'E',
    transclude: true,
    template: '<div class="circle wrapcounter" ng-transclude></div>',
    link: function (scope, iElm, iAttrs, controller) {
      // retreive the inner directive element
      var counter = iElm.find('counter')[0];

      // retreive the inner controller
      var innerController = angular.element(counter).data('$' + 'counter' + 'Controller');

      iElm.on('click', function (e) {
        e.stopPropagation();
        scope.$apply(function () {
          // decorating the increment function with a console log.
          console.log('click wrapper');
          // accessing the inner directive api
          innerController.increment();

        });
      });
    }
  };

});

As you see in any case we have access to the api defined by the inner directive. We could change it, decorate or override existing functions, add our own functions if some are missing from the inner directive, etc…

Hope this makes sense…

Git repo : https://github.com/thaiat/angular-extending-directive/tree/master/scripts

Let me know what you think and happy coding.

Avi Haiat

PS: Big thanks to Nir Kaufman that helped me figure it out.

Angularjs and Requirejs for Very Large Applications

| Comments

NOTE:
This article assumes that you are familiar with both requirejs and angularjs

Live demo is available here : http://thaiat.github.io/directory-angular-ionic-require

Hi all,

I’ve been working with angularjs for the past 3 months.

I like very much the framework, but i had a few issues with what would be the best way to organize the code when writing a large application. Especially i didn’t like editing the index.html each time i would write a new directive, controller or service. So i looked at requirejs and started to think how the 2 libraries would play together.

In a nutshell here is my attempt to combine both angularjs and requirejs.
Some may argue that this is unnecessary, but i wanted to explain my approach and see what is the feedback.
The main reason i needed that is that i’m still using Visual Studio as my editor (yes… i know…). Running the unit test with jasmine in Visual Studio works without an html page, so i needed a way with pure javascript files to link together all my unit tests. requirejs does exactly that…

So, first, let’s see the file structure.

File and folder structure

As per the best practices in angular, i wanted to organize my code by features.
So the folder structure looks like this:

folder structure
1
2
3
4
5
6
7
8
9
10
11
12
13
index.html
scripts/
   main.js
   app/
      area1/
      area2/
   libs/
      angular/
      ionic/
      require/
styles/
   font-awesome/
   main.css

At the root we have scripts and styles, nothing surprising there. In the subfolder scripts/libs, i usually put all the external javascript libraries.
I like to create a subfolder underneath which is named as the current version number of the library (x.y.z).

For example:

folder structure
1
2
3
4
5
6
7
libs/
   angular/
      1.2.9/
           angular-animate.js
           angular-animate.min.js
           angular-cookies.js
           .....

This makes it super easy to try a new version of the library and see if it works, while falling back to the previous one if the new version generates too many bugs.

Right at the root we have the index.html.
Here is the code:

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link href="styles/main.css" rel="stylesheet" />

        <title>Hello World</title>

    </head>

    <body>

        <script src="scripts/libs/require/2.1.10/require.js" data-main="scripts/main"> </script>
    </body>
</html>

As you can see it is also very simple.
It references the needed css, and has only a single script tag, pointing to requirejs and defining scripts/main.js as the entry point for the application.

NOTE:
Note that the .js extension is omitted when referencing a javascript file with requirejs

main.js

This file first defines the libraries needed by the application. We will see later on how we start the application using require entry point function.

So for the moment the content is pretty simple:

scripts/main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require.config({
      paths : {
        'angular' : 'libs/angular/1.2.9/angular',
        'angularUiRouter' : 'libs/angular/angular-ui/0.2.8/angular-ui-router',
        'angularSanitize' : 'libs/angular/1.2.9/angular-sanitize',
        'angularAnimate' : 'libs/angular/1.2.9/angular-animate'

    },
    shim : {
        'angular' : {
            exports : 'angular'
        },
        'angularUiRouter' : {
            deps : ['angular']
        },
        'angularAnimate' : {
            deps : ['angular']
        },
        'angularSanitize' : {
            deps : ['angular']
        }

    }
});

feature folder

For the purpose of the explanation let’s call this feature folder area.
Before we review how we can start the application, let’s first see how a feature is organized.

For each feature we will define a folder containing the following:

area/namespace.js

This file is a returning the namespace of the feature.

scripts/app/area/namespace.js
1
2
3
4
define([], function () {
    'use strict';
    return "area";
});

Note that I usually put in the app folder a root namespace looking like this:

scripts/app/namespace.js
1
2
3
define([], function () {
    return "myApp";
});

In the namespace.js of a specific feature i can require this top root namespace to chain the namespaces. area/namespace.js becomes:

scripts/app/area/namespace.js
1
2
3
4
5
6
define([
    '../namespace'  // we are requiring here the top root namespace
], function (namespace) {
    'use strict';
    return namespace + ".area";
});

I find this elegant because the actual name of the namespace are only hard coded once. I could decide later on to change the namespace and this will not impact any other part of the application…

Next to namespace.js we define 2 others files : module.js and module.require.js.
module.js simply defines a specific module for the feature:

scripts/app/area/module.js
1
2
3
4
5
6
7
8
9
10
define([
        'angular',
        './namespace'
],
    function (angular, namespace) {
        'use strict';

        return angular.module(namespace, []);

    });

See how we reference the namespace for the feature instead of duplicating the hard coded namespace.

module.require.js is the file responsible for binding together all the files we will put in our feature module (filters, directives, services, controllers, etc…)
To achieve a clean separation between those files, we will create sub folders per nature inside the feature folder:

flder structure
1
2
3
4
5
6
7
configs/
constants/
controllers/
directives/
filters/
services/
templates/

Each of these sub folders contains a file per component, plus an _index.js file referencing all of them.
To better understand let see how the services folder looks like:

area/services/_index.js

scripts/app/area/services/_index.js
1
2
3
4
5
define([
    './myService'
], function () {
    'use strict';
});

area/services/myService.js

scripts/app/area/services/myService.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
define([
        '../module',
        '../namespace'
    ],
    function (module, namespace) {
        'use strict';

        var name = namespace + ".myService";
        var dependencies = ['$log'];
        var service = function ($log) {
            return {
                add : function (v1, v2) {
                    $log.debug("add function");
                    return v1 + v2;
                }

            };
        };

        module.factory(name, dependencies.concat(service));
    });

Note that we are again referencing the namespace in order to prefix the name of the service with it.
If you do not want your service to be in a specific namespace you can just ignore that and write:

1
var name = "myService"; // instead of var name = namespace + ".myService";

area/module.require.js

This file simply references all the _index.js per component type

scripts/app/area/module.require.js
1
2
3
4
5
6
7
8
9
10
11
define([
    './configs/_index',
    './constants/_index',
    './controllers/_index',
    './directives/_index',
    './filters/_index',
    './services/_index'
], function () {
    'use strict';

});

Now lets starts our application. We will need an app.js file in scripts/app

scripts/app/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
define([
        'angular',
        './namespace',
        './area/namespace',

        './area/module.require'
    ],
    function (angular, namespace, namespaceArea) {
        'use strict';

        var app = angular.module(namespace, [
            namespaceArea
        ]).run(function () {
            //
        });
        return app;
    });

We are requiring the feature module.require.js file to make sure all the files making the feature are loaded. And in addition we are also referencing the namespace of the application and the namespace of the feature in order to prevent hard coding again their values.

The only thing left is to bind main.js with app.js using the following code at the end of main.js

scripts/main.js
1
2
3
4
5
6
7
8
9
10
require([
        'angular',
        'app/namespace',
        'app/app'
    ],
    function (angular, namespace) {
        angular.element(document).ready(function() {
             angular.bootstrap(document, [namespace]);
       });
    });

This may seem over complicated, but once you understand how it works it is really easy to expand. Each time i need a new “feature”, i’m copying a dummy feature structure, set the namespace, and reference the module.require of the feature in the main app.js. Whenever i need a new component inside the feature, i put it in the correct location based on its type, and edit the corresponding _index.js.

To better illustrate these concepts you can look at this repository.
Live demo is available here : http://thaiat.github.io/directory-angular-ionic-require

I have re-written the Sample Mobile Application of Christophe Coenraets using these technics (see http://coenraets.org/blog/2014/02/sample-mobile-application-with-ionic-and-angularjs/)

Let me know what you think and happy coding.

Avi Haiat