Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Modularization in JavaScript: How to Standardize your Code with IIFE, AMD and RequireJS

See in this article how to standardize and organize your JavaScript code using IIEF modules, AMD and RequireJS.

JavaScript has gained an amazing strength in recent years, and with each passing day, new APIs and frameworks MV* are created by enthusiasts and companies involved with the web community, as was the case with Google AngularJS. It became the protagonist of most of today's Web applications.

Today, it's possible to check "web based" applications like Google Drive, Facebook, Gmail, or still to develop native applications using HTML/CSS/JS combo for Windows and Firefox OS.

With the popularization of collective collaboration between developers, there has been rapid growth in research for improvement of the JavaScript engines, like Google's V8, and code improvements, best practices and searching for better performance of JavaScript applications.

The ECMAScript specification in which JavaScript is based on, is increasingly sophisticated and bringing innovations ranging from syntax sugar to critical performance improvements.

The cost/time of development tends to decrease; Web applications are becoming more sophisticated; recognition of JavaScript as "the language of the Web" makes developers speak the same language.

However, what is the best way to write our code? What is the best pattern? What tools we can use to aid use of these standards?

Dynamic and weakly typed languages such as JavaScript require more discipline at coding time. Unlike static and strongly typed languages, such as Java, that need to be compiled, JavaScript may have changed their behavior at runtime. What's more, in an environment where a number of scripts may have been loaded and run at the same time in competition.

And if such a code declares a variable name that overwrites an existing variable? And if a jQuery plugin that is running in a loop begins to lock the user's browser?

Anyway, many of these issues on competition and parallel execution of activities in the case of front-end universe are relevant and in this article we will see how to apply some of these concepts by building a small application of tasks list, too famous in the web and mobile worlds. But first, let's look at some important concepts for the construction of it.

As important as developing asynchronously is debugging your asynchronous code, using best practices and tools for this purpose. Therefore, take a look at our article "How to Debug Asynchronous JavaScript with Chrome DevTools?" before going further in this one.

Code organization

In many programming languages, you can organize the code through conventions such as Java or frameworks like CodeIgniter for PHP or Django for Python.

JavaScript, on the other hand, suffers from the absence of both styles of organization. To make matters worse, usually the JavaScript layer ends up being a mixture of business rules, communication between the server and the presentation layers, and screen manipulation. This means that it becomes difficult to separate the code into reusable logical categories.

Taking this scenario as a starting point you can see the required implementation of an organization's code so that the developer does not get lost in the implementation as much as flee the basic quality standards. With that in mind, some standards and solutions can be applied to maximize the effects of coding in such a dynamic layer as the client layer.

Namespaces

Some actions can be quite basic and simple, but at the same time help in developing a very good code. In a project with a large number of developers, the risks of overwriting an important component, or a global object are big. It was thinking about these possibilities that, a few years ago, it became popular to use literal objects such as namespaces to avoid conflict of properties.

This practice helped a lot in code organization and separation of responsibilities, allowing certain pattern in the CRUD verbs and other features.

This format is still a practice widely used, and can work well if the entire team is disciplined and understand the importance of this isolation. However, the use of namespaces was still in the window global scope. That is, although the namespaces keep variable names and protected functions within them, they themselves would be contained in the global scope, and could be accidentally overwritten or deleted. In this sense, it is up to the developer to pay particular attention to the source code, when dealing with this type of structure.

Self-running functions or IIEF

Self-running functions or Immediately-Invoked Function Expressions, are functions that are born, perform and die without leaving many traces to the global scope. They basically are not different from conventional functions, with the difference to be executed immediately after being read by the interpreter.

At the same time, they are very useful, among other things, to create modules, isolating attributes that we do not wish to expose to the outside world, giving public visibility only to the relevant components.

This practice is very common in object-oriented languages, where some methods and attributes only make sense in the private scope, and the developer just outsource the methods he wants to expose. This strategy has been applied in different scenarios, and has been very efficient in all of them.

IIEF and boilerplate codes

Boilerplate codes help developers to save time when starting to code. Generally speaking, boilerplate codes are code snippets that end up being continuously repeated during the application development time. In other words, assuming that the adoption of a particular practice or style of coding is beneficial to all, having the boilerplate code makes us not to waste time to write something obvious and we can focus on what really matters. This practice adheres to the famous and desired concept of code reuse.

Today you can find several boilerplate codes for IIFEs, either for the development of modules, jQuery plugins or more complex objects.

AMD: Programming modules in an organized way

We arrived at the point where the IIFEs and modules become part of our daily lives. We define best practices on how to organize the code based on our experience and using boilerplate popular codes.

The constant use of JavaScript in a more advanced form, taught us to use and understand the asynchronous calls and callback functions. We learned that it is a good practice to position our scripts at the end of the HTML document, because the browser stops loading the page to evaluate the content of the scripts.

The improvement of these techniques has motivated initiatives to further enhance the development of web applications. One such initiative is the AMD: Asynchronous Module Definition.

The AMD paradigm suggests that the modules of an application should be loaded asynchronously, respecting their dependencies and managing them, so they will not be charged again if another module requests them.

According to its specification, a number of methods and rules must be followed to ensure the proper functioning of the asynchronous loading and the management betweendependencies and modules. Although not formally a standard, AMD has been widely used and has become popular enough, and it's likely that it will become a requirement for any project in the future.

RequireJS: AMD in practice

One of the implementations that best reflects the use of AMD is the RequireJS API.

Although the RequireJS not follow through to the letter the specifications of AMD, it represents, quite concisely, its operation, taking into account its most important requirements: asynchronous loading of modules and the dependency management.

It consists of a very small js file (about 15kb minified) and is the only file that must be included in the HTML document simply by referencing, in the same script tag, which is the main module of your page.

This main module tells RequireJS what are the modules required for it to work properly. Each of these modules may have their own dependencies from other modules, and so forth.

If you are working on a module managed by RequireJS, you don't have to worry if their dependencies have other addictions. If you are working with an application using the traditional way, you need to know all the dependencies of its modules and add them one by one with script tags in the HTML document in the correct order according to the dependencies.

To-Do List Application

To demonstrate the effectiveness of RequireJS, we will implement the famous To-Do List. This simple application uses AngularJS in the MVC layer, Twitter Bootstrap for visual identity and a small wrapper to work with IndexedDB in a simpler way.

During the application development, several modules will be created, each with its dependencies, all managed by RequireJS.

The folder structure and application files will be the same as shown in Listing 1.

Listing 1. Structure of project directory

+-- css
¦   +-- bootstrap.min.css
¦   +-- bootstrap-theme.min.css
+-- fonts
+-- index.html
+-- js
¦   +-- controllers
¦   ¦   +-- task.js
¦   +-- localdb
¦   ¦   +-- db.js
¦   +-- main.js
¦   +-- todo.js
+-- lib
¦   +-- angular.min.js
¦   +-- angular-route.min.js
¦   +-- bootstrap.min.js
¦   +-- jquery.min.js
¦   +-- require.js
¦   +-- zondb-min.js
+-- partials
  +-- home.html

Some project files have been hidden so we can focus on the most relevant directories of it. The lib directory contains all libraries that the application uses, and the js directory has subdirectories with the controllers, the main file and the IndexedDB library.

At the same level of js directory is the directory partials, with the templates of AngularJS.

As explained above, our application has only one JS file import containing the call to the require.js and a data-main attribute pointing to the main file of the application, as the index.html content shown in Listing 2.

Listing 2. HTML page and the unique include of RequireJS

<!doctype html>
<html>
<head>
  <title>Welcome to the new project</title>
  <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
  <link rel="stylesheet" type="text/css" href="css/bootstrap-theme.min.css"/>
</head>
<body ng-app="Todo">
  <div class="container">
    <div class="row">
      <div class="col-md-2"></div>
      <div class="col-md-8">
        <h1>My TODO List</h1>
        <div ng-view></div>
      </div>
      <div class="col-md-2"></div>
    </div>
  </div>
  <script type="text/JavaScript" data-main="js/main" src="lib/require.js"></script>
</body>
</html>

When RequireJS is included, it will look for the date-main attribute and try to load this file and its dependencies.

It is also in this file, in our case, js/main.js (the .js extension is omitted because the RequireJS assumes that all modules are JavaScript files), where there are the RequireJS settings, such as libraries, paths and mappings as shown in Listing 3.

Listing 3. RequireJS Configuration in the main file

window.name = "NG_DEFER_BOOTSTRAP!";
 
requirejs.config({
  baseUrl: './js',
 
  paths: {
    'bootstrap': '../lib/bootstrap.min',
    'angular': '../lib/angular.min',
    'angular-route': '../lib/angular-route.min',
    'zondb': '../lib/zondb'
  },
 
  shim: {
    'angular': {
      exports: 'angular'
    },
 
    'angular-route': {
      deps: ['angular']
    },
 
    'zondb': {
      exports: 'zonDB'
    }
  }
});
 
require(['todo'], function(todo) {
  angular.element().ready(function() {
      angular.resumeBootstrap();
  });
});

The requirejs.config function takes an object as an argument to the application settings, and how RequireJS should handle dependencies.

The basePath attribute tells from which the modules directory should be loaded without the need to specify a path. You can still use relative paths "below" the base directory, however, this should be a way of easy access to the application files.

The paths attribute maps libraries we need to use, by what name they will be recognized and what directory they are.

It is possible, for example, to map more than one version of the same library with different names, as RequireJS will allow only a unique module name in its context. This ensures no name collisions, avoiding the loss of modules that would be overwritten, and whose problem it would be very difficult to detect afterwards.

The shim attribute includes global context libraries within the AMD context. jQuery, AngularJS and other frameworks reside in the global context window, which goes against the paradigm of AMD. To resolve this problem, we created these shims, that import these frameworks imitating the AMD behavior.

After applying the configuration, we use the require function to execute the main.js, carrying all its dependencies. This function takes two arguments: the first is an array of optional strings with the list of modules which main.js depends, and the second argument is a callback function that effectively perform our main program.

For each module we include into the array of strings, each loaded module is an argument passed to the callback function.

In the case of our main.js, it is carrying the todo.js module (remember that RequireJS assumes that all modules are js files, and so we should not explain the extension of importing the same).

Let's take a look at todo.js module, as shown in Listing 4.

Listing 4. Module todo.js

define(['angular', 'controllers/task', 'angular-route'], 
  function(ng, Task) {
 
  var Todo = ng.module('Todo', ['ngRoute']);
  Todo.controller('Task', Task);
 
  Todo.config(function($routeProvider, $locationProvider) {
    $routeProvider.when('/', {
      templateUrl: 'partials/home.html',
      controller: Task
    });
  });
 
  return Todo;
});

The todo.js module creates a module of AngularJS called Todo and returns it to the final of the function define, of RequireJS. Note that the function define works the same way that the require function, but its purpose is to export a module, whereas the function require aims to execute instructions, rather than export them.

Because it is a AngularJS module, it depends on the libraries Angular and ngRoute, and also carries a controller called Task.

All these facilities are being loaded into the first argument of the require function, and the relevant objects (note that we do not specify a reference to the ngRoute because we won't use it explicitly) are passed as arguments to the callback function, so that it can be used in the definition of the Todo module.

Both angular as angular-routeare modules configured as libraries that our application depends, however the module task has been developed exclusively for the application.

Note that, by being in another directory, your path must be detailed in the list of modules from the basePath we defined earlier.

The controllers/task.js file contents is shown in Listing 5.

Listing 5. Module controllers/task.js

define(['angular', '../localdb/db'], function(ng, db) {
  function Task($scope, $rootScope, $timeout) {
    $scope.title = "My tasks";
    $scope.taskName = null;
    $scope.tasks = null;
    $scope.taskId = null;
 
    function getRowIndex(taskId) {
      var i=0,
          len = $scope.tasks.length;
 
      for(i; i < len; i++) {
        if($scope.tasks[i].id == taskId) {
          return i;
        }
      }
    }
 
    function loadTasks() {
      db.query('tasks', function(res) {
        $scope.tasks = res;
        $scope.$digest();
      });
    }
 
    $scope.saveTask = function() {
      var task = {name: $scope.taskName};
 
      if($scope.taskId) {
        task.id = $scope.taskId;
        updateTask(task);
        return;
      }
 
      db.addRow('tasks', task, function(data) {
          task.id = data;
          $scope.tasks.push(task);
          $scope.taskName = null;
          $scope.$digest();
        });
    };
 
    function updateTask(task) {
      db.updateRow('tasks', task, function(data) {
        $scope.taskName = '';
        $scope.taskId = null;
        var idx = getRowIndex(data);
        $scope.tasks[idx] = task;
        $scope.$digest();
      });
    }
 
    $scope.removeTask = function(taskId) {
      $scope.tasks.forEach(function(obj, idx) {
        if(obj.id == taskId) {
          db.deleteRow('tasks', taskId, function() {
            $scope.tasks.splice(idx, 1);
            $scope.$digest();
          });
        }
      });
    };
 
    $scope.editTask = function(taskId) {
      $scope.taskId = taskId;
      var task = $scope.tasks.filter(function(task, idx) {
        if(task.id == taskId) return task;
      })[0];
 
      $scope.taskName = task.name;
    };
 
    loadTasks();
 
  }
 
  return Task;
});

This module is a bit longer, because it has the communication interface with the data manipulation library IndexedDB and all CRUDs, as well as the glue between the data and the presentation layer through the scope.

The Task module essentially depends on the libraries responsible for these actions, that is, the angular and the wrapper of the IndexedDB.

Note that during the implementation of the application, many modules dependencies are repeated. This is solved internally by RequireJS, that is, it makes the control modules that have already been loaded, preventing them from being loaded again.

If any of the modules to be loaded has other dependencies, it is also solved by RequireJS that will do the asynchronous and on-demanddownload of the files only when necessary.

In the end, if you deploy this application on any web server, you have a result similar to the shown in Figure 1.

Figure 1. Application Screen

The application basically takes care of adding tasks, edit them and remove them, basic actions of a CRUD, but using what's more organized when it comes to JavaScript in conjunction with the RequireJS.

Optimization of the modules

Also, it is important to mention that the RequireJS has an optimizer tool called r.js.

This tool must be downloaded separately and depends on other tools to be executed like, for example, Node.js.

With it you can, for example, join interdependent modules and create a single minified file. For example, suppose you have a user module, which was separated into layers view, presentation, and model with the actions of a CRUD.

These two sub-modules only make sense for your primary user module. Thus, as prepared for the production environment, they can safely be united into a single file and minified.

The r.js is able to perform this merge without harming the dependencies that you created during the development phase. It works something like the r.js compile your application, reducing the amount of files and their sizes, and depending on the form used to minify files, it can even optimize your code.

This is another handy tool for developers that will significantly facilitate development and code maintenance, and would imply performance and optimization improvements.

Conclusion

Much has been discussed and implemented on the self-running functions IIFEs and the appearance of modules, what made us closer to what could become a pattern of development.

The experience gained by developers and improvement of modules and the use of IIFEs led to the creation and expansion of the AMD, which reinforces the use of modules that can be loaded asynchronously, and managed intelligently.

The responsibility to maintain clean and organized code belongs, of course, to the developer and AMD can do it by itself. This article has shown through the development of a small application, that small parties can indeed make a big difference in the end.

Links

RequireJS Website: http://requirejs.org/

RequireJS API Docs: http://requirejs.org/docs/api.html

AMD (Asynchronous Module Definition): https://github.com/amdjs/amdjs-api/wiki/AMD



Fabrí­cio Galdino is a software expert and has worked with IT analysis and business development for more than five years. It has extensive experience with testing, back and front-end technologies.

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login