Skip to main content

Angular + Require project template

I've been working on an Angular project for good number months. There's a lot that I've learned and I've realized that the code I wrote could have been structured in a better way. I wanted to write this post sometime back, however I thought it would be better to create an application structure first and then post my learnings. I have posted all the code on github here - https://github.com/sagar-ganatra/angular-require-template. The template is still a work in progress, however the basic structure of the template is in place.


Motivation:
I always visualized a web page broken down into multiple components that are completely decoupled and components that can be reused. For example, a component or a widget that displays a chart does not have to know about the navbar in the page. It would however provide an interface through which external components would interact or change the state of it.

Why RequireJS?
In my Angular application's index.html file, I had included various script blocks that looked like this:

<script src="../bower_components/jquery/jquery.js"></script>
<script src="../bower_components/angular/angular.js"></script>
<script src="../bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="../bower_components/angular-cookies/angular-cookies.js"></script>
<script src="../bower_components/angular-route/angular-route.js"></script>

Here the script tags are included in such a way that the dependent files come first and then the others are listed. Every time I added a new script file in the project, I had to update the index.html file and ensure that the dependencies are always included first. Instead, I could use a RequireJS shim to declare the dependencies:

shim: {
    angular: {
        deps: ['jquery'],
        exports: 'angular'
    },
        
    angularMocks: {
        deps: ['angular']  
    },
        
    angularCookies: {
        deps: ['angular']
    },
        
    uirouter: {
        deps: ['angular']
    }
}


This is more declarative and less prone to errors.

This is not the only reason I would use RequireJS in my project, one of the other factors and the most important one is to provide structure to the application. I want my components to be independent or individual widgets that can be reused. When creating components I would make use of the directive interface which can then be included anywhere in the project. For example, the interface for a navbar could look like:

<navbar title="{{title}}"></navbar>

Here the navbar element has the title attribute which would then become a scope variable for the navbar directive. Essentially everything on the page would be a collection of directives which would then be compiled and the resultant HTML markup would be injected into the DOM:


Here the directive serves as a gateway to the service that we are trying to add to the application. A directive is an individual widget which is not aware of other directives used in the page and it's service agnostic. Here in our example, the playersList directive would consume list of players and perform some action when a player is selected. The directive would provide an interface and the page that wants to use the directive can then provide the data:

<div players-list
     players="players"
     on-player-select="showPlayerStats(playerId)"
     selected-player="selectedPlayerId">
</div>

Here the values provided to various attributes are defined in the controller scope. In this way, the directive remains agnostic of the data or the service that it should talk to.

In my project template, I've create a module for each directive. In this example, we would have two modules - navbarModule, playersStatsModule. I would include playersList and playerDetails directive into one single module because they serve as one entity. However, they are completely decoupled and don't interact with each other directly. The idea is to create modules that represent one single entity, which in this case is playerStats. The module definition would look like this:

define([
    'angular',
    'components/playerStats/directives/playersListDirective',
    'components/playerStats/directives/playersDetailDirective',
    'domain/Players'
],  function(ng, playersListDirective, playersDetailDirective, Players) {
        'use strict';
        
        return ng.module('playerStatsModule', [])
                 .directive('playersList', playersListDirective)
                 .directive('playersDetail', playersDetailDirective) 
                 .service('Players', Players);
                 
    }
);

Notice that there's one file playerStatsModule.js which declares dependency on the directives and the service. Also, the name is to the directive here in the module definition. This approach is the flip side of the approach that is generally followed when defining directives. Generally one would create a module first and then use the module reference in their directive definition; but the approach that I mentioned above makes the code more cleaner. Here's how the definition of the playersList directive would look like:

define(function() {
    'use strict';
    
    return function () {
        return {
            restrict: 'AE',
            templateUrl: './components/playerStats/partials/playersList.html',
            scope: {
                onPlayerSelect: '&',
                players: '=',
                selectedPlayer: '='
            }
        };
    };
});

It returns a function and then in the module a name is assigned to the directive.

Application structure:

/app
  - app.js
  - main.js
  - bootstrap.js
  - index.html
  /components
      /auth
      /login
      /navbar
      /playerStats
           /directives
                - playersDetailsDirective.js
                - playersListDirective.js
           /partials
                - playersDetails.html
                - playersList.html
  /domain
       -  Players.js
       -  User.js
  /modules
        - authModule.js
        - loginModule.js
        - navbarModule.js
        - pagesModule.js
        - playerStatsModule.js
  /pages
      /home
          /controllers
               - playersListController.js
               - playersStatsController.js
          /partials
               - playersList.html
               - playersStats.html
      /login

Notice that the services responsible for sending requests to the server are listed under domain. The pages directory lists various pages in the application and the partials listed under each page contain references to these components. In this way, pages handle the application logic (events, querying the service, etc) and individual component directives handle rendering logic.

In app.js, a module for the application is created. This module will declare dependency on various other modules in the source code in addition to third party modules:

define([
    'angular',
    /*mock module*/
    'mockModule',
    'mockServices',
    /* user defined modules */
    'modules/authModule',
    'modules/loginModule',
    'modules/navbarModule',
    'modules/playerStatsModule',
    /* pages module */
    'modules/pagesModule',
    /* include vendor dependencies here */
    'angularCookies',
    'angularMessages',
    'uirouter',
    'lumx',
], function (ng) {
    'use strict';
    
    var app = ng.module('playersApp', [
        'ngCookies',
        'ui.router',
        'lumx',
        'mockModule',
        'authModule',
        'loginModule',
        'navbarModule',
        'playerStatsModule',
        'pagesModule'
    ]);
});

The main.js file declares the shim configuration (mentioned before) and bootstrap.js would bootstrap the application by calling angular.bootstrap on the document object.

Other Goodies:

I'm taking help from lumx to implement Material design, Angular UI router to define states/routes in the application, Angular Mocks to mock services and Gulp for build.

Pending:

Unit testing using Jasmine and Karma.
Structure styles (CSS/SCSS).
Refine Gulp build system.
CI/travis.yaml

You can clone the repository and see how the application is structured and how it is currently styled.


Comments

  1. This is a Great Article ever i see... thank's for sharing :D

    Cara Bermain Judi

    ReplyDelete
  2. And this site contains even more information regarding this issue.

    ReplyDelete

Post a Comment

Popular posts from this blog

Custom validation messages for HTML5 Input elements using the constraint validation API

HTML5 has introduced several input types such as EMAIL, URL, RANGE, SEARCH, DATE, TIME, etc,. Most of the modern browsers have implemented them and are ready to be used in a HTML document. Another exciting feature introduced in HTML5 is the form validation. Instead of writing JavaScript to validate users input, browsers can now validate it and show an appropriate message if the validation fails. The validation message is shown in line with the field for which the validation has failed. The default error message is shown when the validation fails. In this post I'll explain how these error messages can be changed.

Adding beforeRender and afterRender functions to a Backbone View

I was working on a Backbone application that updated the DOM when a response was received from the server. In a Backbone View, the initialize method would perform some operations and then call the render method to update the view. This worked fine, however there was scenario where in I wanted to perform some tasks before and after rendering the view. This can be considered as firing an event before and after the function had completed its execution. I found a very simple way to do this with Underscore's wrap method.

De-obfuscating javascript code in Chrome Developer Tools

I had blogged about JavaScript debugging with Chrome Developer Tools some time back, wherein I have explained how these developer tools can help in debugging javascript code. Today Google Chrome 12 was released and my Chrome browser was updated to this version. As with every release, there have been some improvements made on performance, usability etc,. One feature that stood out for me is the ability to De-obfuscate the javascript code.

What is Minification?

Minification is the process of removing unnecessary characters such as white spaces, comments, new lines from the source code. These otherwise would be added to make the code more readable. Minifying the source code helps in reducing the file size and thereby reducing the time taken to download the file. This is the reason why most of the popular javascript libraries such as jQuery are minified. A minified jQuery file is of 31 KB in size where as an uncompressed one is about 229 KB. Unfortunately, debugging minified javascript file…