Wednesday, May 28, 2014

Fundamental Concepts Within Ember.js

Ember.js, a client-side JavaScript web application framework. Ember.js (we simple call it Ember) helps in the development of Single-Page Applications (SPA) in a Reactive fashion.

Before moving on further, lets pose the below question:

Why develop a web app as supposed to a native GUI app?
Web application, technically means => The ability to bookmark and share URLs in as stateless fashion i.e. a superior shareability and collaboration as supposed to a native GUI app. Hence, the justification to develop web applications.

Ember as a SPA framework, its strengths are in combining tools and concepts of native GUI frameworks with support for URL. Therefore, making URL as a first-class citizen rather than an after thought.

To be an Ember literate, one has to deeply understand its native vocabulary-set/concepts, which can be abbreviated as TRC-MRC-VH and stands for:

T - Templates
R - Router
C - Components

M - Models
R - Route
C - Controllers

V - Views
H - Helpers




Below is my interpretation of the above concepts & their relationships to each other, as of Ember version 1.5.1.


Templates

Templates describe the apps user interface.

Features:
 a. Each template is backed by a model and automatically updates itself if the model changes.
 b. Templates are composed of one or more of the following: HTML, Expressions, Outlets, & Components. Where:
  i. Expressions: Refers to data-binding expressions, for e.g. {{firstName}}. Expressions once declared in the templates, it gets data-bound to respective model's/controller's properties.
  ii. Outlets: Refers to placeholders, into which templates are injected. Here, router is responsible for injecting templates into outlets.
  iii. Components: Refers to custom HTML elements, for e.g. {{#blog-post title=title}}. Which could to used for:
    1. Cleaning-up repetitive templates
    2. Creating reusable controls
 c. A template can optionally have a controller in addition to a model and can retrieve properties from both.
 d. Handlebars is used as the primary templating engine.
 e. On-demand pre-compilation of templates are possible. This allows organisation of the templates as separate .hbs or .handlebars files. Or directly declare following script tag => type="text/x-handlebars" in your HTML.

Example: JS Bin example of the above points (a, b.i, b.ii, c, d, e)


Router

A Router translates an URL into a series of nested templates. Which means, router is responsible for injecting templates into above mentioned outlets.


Components

Component is a custom HTML tag, which enables you to create reusable controls to the above mentioned templates.
  
Implementation aspects:
 a. Appearance is describe using Handlebars templates
 b. Behaviour is implemented using JavaScript

Example: Section on Showing confirmation modal-dialog in the tutorial Ember.js + Sails.js + PostgreSQL is an use case of components.


Models

Model is an object that stores persistent state.

Features:
 a. Models are not directly displayed to the users, however template takes care of displaying them in HTML format.
 b. Models are usually loaded/updated with data from/to the backend services asynchronously. However, model is agnostic to any backend/mockup adapters like Ember-Data, Ember-Model, Ember-Resource, etc.

Example: JS Bin example of the above points: (a, b)



Route

A route is an object that tells the template which model it should display.

Features:
 a. Route query's the model using model-hook, so that the model is available to template and its controller
 b. Route returns promise from model-hook, so one can implement a 'LoadingRoute' (i.e. a waiting state, which waits for the model to resolve asynchronously).
 c. Route can set controller properties
 d. Route can execute events and actions
 e. Route can connect any template to any controller

Example: JS Bin examples of the above points: (a, c), (b) & (d, e)


Controllers

A controller is an object that stores application state.

Features:
 a. A controller is in direct relation with a given template. However, controller is optional for a given template.
 b. Controller houses properties that are retrievable by its template.
 c. Receives a model from a Route
 d. Acts as a bridge between model and view/template
 e. Houses convenient methods (i.e. business-logic). For e.g. methods that switch between editing mode and normal mode. Methods such as 'goIntoEditMode()' and 'closeEditMode()'.
 f. Good to know! When a controller is not declared for a given template, then the Ember framework auto-generates a controller. For e.g. a user template with a 'UserRoute', without a 'UserController' implementation artifact (i.e. there is no need for a explicit business-logic), then the Ember will generate a controller internally (i.e. in memory). Ember Inspector extension for Chrome web-browser / Ember Inspector add-on for Firefox web-browser can enable you to track internally/explicitly created controllers.

Example: Tutorial covering Ember.js + Sails.js + PostgreSQL is an example of the above points: (a, b, c, d, e) & (f)


Views

Views represent visual elements of an application.

Features:
 a. Views are composition of Controller + Handlebars template + Route
 b. Views generate events and enable interactions with visual elements
 c. Views comes with a collection of native hooks, such as 'didInsertElement' hook, used for implementing complex interaction using jQuery
 d. View composition facilitates reusability, such as modals, popovers, date-pickers & autocomplete fields.

Example: Tutorial covering Ember.js + Sails.js + PostgreSQL is an example of the above points: (a, c) & (b, d)


Helpers 

Helpers refers to Handlebars helpers.

Features:
 a. A function that transform data just before rendering. For e.g., transformation of the following date/timestamp: Wed May 28 10:10:01 EEST 2014 => '28 May 2014'.

Example: JS Bin example of the above point: (a)


Reference:
 - An In-Depth Introduction To Ember.js
 - Alternatives to Ember Data
 - Emberjs (Offical Website)
 - Emberjs Core-concepts (Official Documentation)

Thursday, May 1, 2014

Basics of Grunt: A task runner that helps automate your bulid-processes in your web development project



This blog covers couple of "elementary" use-cases for Grunt task runner. That is to automate certain repetitive build-processes in your project. Here the build-processes refers to invoking transpiler, JavaScript files concatenation & its minification.
Presumptions:
  • CoffeeScript is used instead of regular JavaScript in your web development project
  • Node.js runtime is installed
  • npm package manager for Node.js is also installed
Table of contents

Grunt task runner installation

Running the below command will install Grunt globally (i.e. accessible to Node.js runtime):
$ sudo npm install grunt-cli -g


Use-case 1

Grunt task runner for automating transpilation


i. Create source code folders

Starting with an empty project folder, create these two folders in your project's root directory:
  • js
  • coffeescript

ii. Install Grunt into your project

In your project's root folder, run the below command:
$ npm install grunt

iii. Generate 'package.json' file

This 'package.json' is required by the npm package manager. Now, run the below command & answer the project related meta-data as you find fit:
$ npm init

iv. Create 'Gruntfile.js' file

Now, in the project's root folder, create the must needed 'Gruntfile.js' file, with following modules definition skeleton:
module.exports = function(grunt) {
    grunt.initConfig({});
    grunt.loadNpmTasks();
};

v. Install & configure CoffeeScript-to-JavaScript transpiler module

Now, with the help of npm, we install a Grunt module for CoffeeScript-to-JS transpilation, with the below command:
$ npm install grunt-contrib-coffee --save-dev
Now, in the above created 'Gruntfile.js' file add the following module-handling definitions:
module.exports = function(grunt) {
    grunt.initConfig({
        coffee: {
            options: {
                bare: true
            },
            scritps: {
               expand: true,
               flatten: true,
               cwd: 'coffeescript/',
               src: ['*.coffee'],
               dest: 'js/',
               ext: '.js'
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-coffee');
};
Now, in-order to run the transpiler against your project's CoffeeScript, run the below command:
$ grunt coffee

vi. Automate transpilation

Now, the following two steps are necessary to automate-the-invocation of CoffeeScript-to-JS transpiler:
NOTE! This transpilation takes place only for the modified source files by Grunt.
Step1: Install modules that help in auto-running
Install these modules 'grunt-contrib-watch' & 'grunt-newer' with the below command:
$ npm install grunt-contrib-watch grunt-newer --save-dev
Step2: Configure Gruntfile.js file
Add the below definitions to 'Gruntfile.js' file:
module.exports = function(grunt) {
    grunt.initConfig({
        coffee: {
            options: {
                bare: true
            },
            scritps: {
                expand: true,
                flatten: true,
                cwd: 'coffeescript/',
                src: ['*.coffee'],
                dest: 'js/',
                ext: '.js'
            }
        },
        watch: {
            options: {
                livereload: true
            },
            scritps: {
                files: ['coffeescript/*.coffee'],
                tasks: ['newer:coffee']
            }
       }
    });

    grunt.loadNpmTasks('grunt-contrib-coffee');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-newer');
};
Now, in-order to auto-run the transpiler against your project's CoffeeScript, run the below command:
$ grunt watch


Use-case 2

Grunt task runner for automating concatenation & minification


i. Create a distribution folder

Now, in the project's root folder, create the below folder:
  • distribution

ii. Install & configure Concatenation & Minification modules

Now, with the help of npm, install the below two Grunt modules. These modules can concatenate JS files into a single-file & minify it. So, in-order to install these two modules run this command:
$ npm install grunt-contrib-concat grunt-contrib-uglify --save-dev
Add the below definitions to 'Gruntfile.js' file:
module.exports = function(grunt) {
    grunt.initConfig({
        coffee: {
            options: {
                bare: true
            },
            scritps: {
               expand: true,
               flatten: true,
               cwd: 'coffeescript/',
               src: ['*.coffee'],
               dest: 'js/',
               ext: '.js'
            }
        },
        watch: {
            options: {
                livereload: true
            },
            scritps: {
                files: ['coffeescript/*.coffee'],
                tasks: ['newer:coffee']
            }
        },
        concat: {
            dist: {
                src: ['js/*.js'],
                dest: 'distribution/js/all.js'
            }
        },
        uglify: {
            dist: {
                options: {
                    banner: '/* Project-Template Shaped With Grunt Task Runner by Nash | May 2014 */\n'
                },
                files: {
                    'distribution/js/all.min.js': ['distribution/js/all.js']
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-coffee');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-newer');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');
};
Validate the above definitions does work by running these commands:
$ grunt concat
$ grunt uglify

iii. Automate build-sequence

Now, automating the invocation of the following build-sequence by Grunt: Transpilation-> Concatenation-> Minification of source files.
Configure Gruntfile.js file
Add the below definitions to 'Gruntfile.js' file in-order help setup the build-sequence:
module.exports = function(grunt) {
    grunt.initConfig({
        coffee: {
            options: {
                bare: true
            },
            scritps: {
               expand: true,
               flatten: true,
               cwd: 'coffeescript/',
               src: ['*.coffee'],
               dest: 'js/',
               ext: '.js'
            }
        },
        watch: {
            options: {
                livereload: true
            },
            scritps: {
                files: ['coffeescript/*.coffee'],
                tasks: ['autobuild-with-minification']
            }
        },
        concat: {
            dist: {
                src: ['js/*.js'],
                dest: 'distribution/js/all.js'
            }
        },
        uglify: {
            dist: {
                options: {
                    banner: '/* Project-Template Shaped With Grunt Task Runner by Nash | May 2014 */\n'
                },
                files: {
                    'distribution/js/all.min.js': ['distribution/js/all.js']
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-coffee');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-newer');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');

    grunt.registerTask('autobuild-with-minification', ['newer:coffee', 'concat', 'uglify']);
    grunt.registerTask('default', ['coffee', 'concat', 'uglify', 'watch']);
};
Now, in-order to auto-run build-sequence, such as Transpilation-> Concatenation-> Minification, run the below command:
$ grunt
NOTE! You can further minify your JS file, if your 'Gruntfile.js' definition does not include this definition under 'coffee':
options: {
   bare: true
}


External references