Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Mastering JavaScript Single Page Application Development
Mastering JavaScript Single Page Application Development

Mastering JavaScript Single Page Application Development: An in-depth guide to building scalable and maintainable single-page applications in JavaScript

eBook
$35.98 $39.99
Paperback
$48.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Mastering JavaScript Single Page Application Development

Chapter 1. Getting Organized with NPM, Bower, and Grunt

JavaScript was the bane of the web development industry during the early days of the browser-rendered Internet. It now powers hugely impactful libraries such as jQuery, and JavaScript-rendered (as opposed to server-side-rendered) content is even indexed by many search engines. What was once largely considered an annoying language used primarily to generate pop-up windows and alert boxes, has now become arguably the most popular programming language in the world.

Not only is JavaScript now more prevalent than ever in frontend architecture, but it has become a server-side language as well, thanks to the Node.js runtime. We have also seen the proliferation of document-oriented databases, such as MongoDB, which store and return JSON data. With JavaScript present throughout the development stack, the door is now open for JavaScript developers to become full-stack developers without the need to learn a traditional server-side language. Given the right tools and know-how, any JavaScript developer can create single page applications comprised entirely of the language they know best, and they can do so using an architecture like MEAN (MongoDB, Express, AngularJS, and Node.js).

Organization is key to the development of any complex Single Page Application (SPA). If you don't get organized from the beginning, you are sure to introduce an inordinate number of regressions to your app. The Node.js ecosystem will help you to do this with a full suite of indispensable and open-source tools, three of which we will discuss here.

In this chapter, you will learn about:

  • Node Package Manager (NPM)
  • Bower frontend package manager
  • Grunt JavaScript task runner
  • How these three tools can be used together to create an organized development environment that is ideal for creating an SPA and is essential to the MEAN stack architecture.

What is Node Package Manager?

Within any full-stack JavaScript environment, Node Package Manager will be your go-to tool for setting up your development environment and for managing server-side libraries. NPM can be used within both global and isolated environment contexts. We will first explore the use of NPM globally.

Installing Node.js and NPM

NPM is a component of Node.js, so before you can use it you must first install Node.js. You can find installers for both Mac and Windows at nodejs.org. Once you have Node.js installed, using NPM is incredibly easy and is done from the Command Line Interface (CLI). Start by ensuring you have the latest version of NPM installed, as it is updated more often than Node.js itself:

$ npm install -g npm

When using NPM, the -g option will apply your changes to your global environment. In this case, you want your version of NPM to apply globally. As stated previously, NPM can be used to manage packages both globally and within isolated environments. In the following, we want essential development tools to be applied globally so that you can use them in multiple projects on the same system.

Tip

With Mac and some Unix-based systems, you may have to run the npm command as the superuser (prefix the command with sudo) in order to install packages globally, depending on how NPM was installed. If you run into this issue and wish to remove the need to prefix npm with sudo, see docs.npmjs.com/getting-started/fixing-npm-permissions.

Configuring your package.json file

For any project you develop, you will keep a local package.json file to manage your Node.js dependencies. This file should be stored at the root of your project directory and it will only pertain to that isolated environment. This allows you to have multiple Node.js projects with different dependency chains on the same system.

When beginning a new project, you can automate the creation of the package.json file from the command line:

$ npm init

Running npm init will take you through a series of JSON property names to define through command line prompts, including your app's name, version number, description, and more. The name and version properties are required, and your Node.js package will not install without them defined. Several of the properties will have a default value given within parentheses in the prompt so that you may simply hit Enter to continue. Other properties will simply allow you to hit Enter with a blank entry and will not be saved to the package.json file, or will be saved with a blank value:

name: (my-app)
version: (1.0.0)
description:
entry point: (index.js)

The entry point prompt will be defined as the main property in package.json and is not necessary unless you are developing a Node.js application. In our case, we can forgo this field. The npm init command may in fact force you to save the main property, so you will have to edit package.json afterward to remove it; however, that field will have no effect on your web app.

You may also choose to create the package.json file manually using a text editor, if you know the appropriate structure to employ. Whichever method you choose, your initial version of the package.json file should look similar to the following example:

{ 
  "name": "my-app", 
  "version": "1.0.0", 
  "author": "Philip Klauzinski", 
  "license": "MIT", 
  "description": "My JavaScript single page application." 
} 

If you want your project to be private and want to ensure that it does not accidently get published to the NPM registry, you may want to add the private property to your package.json file, and set it to true. Additionally, you may remove some properties that only apply to a registered package:

{ 
  "name": "my-app", 
  "author": "Philip Klauzinski", 
  "description": "My JavaScript single page application.", 
  "private": true 
} 

Once you have your package.json file set up the way you like it, you can begin installing Node.js packages locally for your app. This is where the importance of dependencies begins to surface.

NPM dependencies

There are three types of dependencies that can be defined for any Node.js project in your package.json file: dependencies, devDependencies, and peerDependencies. For the purpose of building a web-based SPA, you will only need to use the devDependencies declaration.

devDependencies are those which are required for developing your application, but not required for its production environment or for simply running it. If other developers want to contribute to your Node.js application, they will need to run npm install from the command line to set up the proper development environment. For information on the other types of dependencies, see docs.npmjs.com.

When adding devDependencies to your package.json file, the command line again comes to the rescue. Let's use the installation of Browserify as an example:

$ npm install browserify --save-dev

This will install Browserify locally and save it along with its version range to the devDependencies object in your package.json file. Once installed, your package.json file should look similar to the following example:

{ 
  "name": "my-app", 
  "version": "1.0.0", 
  "author": "Philip Klauzinski", 
  "license": "MIT", 
  "devDependencies": { 
    "browserify": "^12.0.1" 
  } 
} 
 

The devDependencies object will store each package as a key-value pair in which the key is the package name and the value is the version number or version range. Node.js uses semantic versioning, where the three digits of the version number represent MAJOR.MINOR.PATCH. For more information on semantic version formatting, see semver.org.

Updating your development dependencies

You will notice that the version number of the installed package is preceded by a caret (^) symbol by default. This means that package updates will only allow patch and minor updates for versions above 1.0.0. This is meant to prevent major version changes from breaking your dependency chain when updating your packages to the latest versions.

To update your devDependencies and save the new version numbers, you can enter the following from the command line:

$ npm update --save-dev

Alternatively, you can use the -D option as a shortcut for --save-dev:

$ npm update -D

To update all globally installed NPM packages to their latest versions, run npm update with the -g option:

$ npm update -g

For more information on semantic versioning within NPM, see docs.npmjs.com/misc/semver.

Now that you have NPM set up and you know how to install your development dependencies, you can move on to installing Bower.

Bower

Bower is a package manager for frontend web assets and libraries. You will use it to maintain your frontend stack and control version chains for libraries such as jQuery, AngularJS, and any other components necessary to your app's web interface.

Installing Bower

Bower is also a Node.js package, so you will install it using NPM, much like you did with the Browserify example installation in the previous section, but this time you will be installing the package globally. This will allow you to run bower from the command line anywhere on your system without having to install it locally for each project:

$ npm install -g bower

You can alternatively install Bower locally as a development dependency so that you may maintain different versions of it for different projects on the same system, but this is generally not necessary:

$ npm install bower --save-dev

Next, check that Bower is properly installed by querying the version from the command line:

$ bower -v

Bower also requires a Git version control system, or VCS, to be installed on your system in order to work with packages. This is because Bower communicates directly with GitHub for package management data. If you do not have Git installed on your system, you can find instructions for Linux, Mac, and Windows at git-scm.com.

Configuring your bower.json file

The process of setting up your bower.json file is comparable to that of the package.json file for NPM. It uses the same JSON format, has both dependencies and devDependencies, and can also be automatically created:

$ bower init

Once you type bower init from the command line, you will be prompted to define several properties with some defaults given within parentheses:

? name: my-app 
? version: 0.0.0 
? description: My app description. 
? main file: index.html 
? what types of modules does this package expose? (Press <space> to? what types of modules does this package expose? globals 
? keywords: my, app, keywords 
? authors: Philip Klauzinski 
? license: MIT 
? homepage: http://gui.ninja 
? set currently installed components as dependencies? No 
? add commonly ignored files to ignore list? Yes 
? would you like to mark this package as private which prevents it from being accidentally published to the registry? Yes 

Tip

These questions may vary, depending on the version of Bower you install.

Most properties in the bower.json file are not necessary unless you are publishing your project to the Bower registry, indicated in the final prompt. You will most likely want to mark your package as private, unless you plan to register it and allow others to download it as a Bower package.

Once you have created the bower.json file, you can open it in a text editor and change or remove any properties you wish. It should look something like the following example:

{ 
  "name": "my-app", 
  "version": "0.0.0", 
  "authors": [ 
    "Philip Klauzinski" 
  ], 
  "description": "My app description.", 
  "main": "index.html", 
  "moduleType": [ 
    "globals" 
  ], 
  "keywords": [ 
    "my", 
    "app", 
    "keywords" 
  ], 
  "license": "MIT", 
  "homepage": "http://gui.ninja", 
  "ignore": [ 
    "**/.*", 
    "node_modules", 
    "bower_components", 
    "test", 
    "tests" 
  ], 
  "private": true 
} 

If you wish to keep your project private, you can reduce your bower.json file to two properties before continuing:

{ 
  "name": "my-app", 
  "private": true 
} 

Once you have the initial version of your bower.json file set up the way you like it, you can begin installing components for your app.

Bower components location and the .bowerrc file

Bower will install components into a directory named bower_components by default. This directory will be located directly under the root of your project. If you wish to install your Bower components under a different directory name, you must create a local system file named .bowerrc and define the custom directory name there:

{ 
  "directory": "path/to/my_components" 
} 

An object with only a single directory property name is all that is necessary to define a custom location for your Bower components. There are many other properties that can be configured within a .bowerrc file. For more information on configuring Bower, see bower.io/docs/config/.

Bower dependencies

Bower also allows you to define both the dependencies and devDependencies objects like NPM. The distinction with Bower, however, is that the dependencies object will contain the components necessary for running your app, while the devDependencies object is reserved for components that you might use for testing, transpiling, or anything that does not need to be included in your frontend stack.

Bower packages are managed using the bower command from the CLI. This is a user command, so it does not require super user (sudo) permissions. Let's begin by installing jQuery as a frontend dependency for your app:

$ bower install jquery --save

The --save option on the command line will save the package and version number to the dependencies object in bower.json. Alternatively, you can use the -S option as a shortcut for --save:

$ bower install jquery -S

Next, let's install the Mocha JavaScript testing framework as a development dependency:

$ bower install mocha --save-dev

In this case, we will use --save-dev on the command line to save the package to the devDependencies object instead. Your bower.json file should now look similar to the following example:

{ 
  "name": "my-app", 
  "private": true, 
  "dependencies": { 
    "jquery": "~2.1.4" 
  }, 
  "devDependencies": { 
    "mocha": "~2.3.4" 
  } 
} 

Alternatively, you can use the -D option as a shortcut for --save-dev:

$ bower install mocha -D

You will notice that the package version numbers are preceded by the tilde (~) symbol by default, in contrast to the caret (^) symbol as is the case with NPM. The tilde serves as a more stringent guard against package version updates. With a MAJOR.MINOR.PATCH version number, running bower update will only update to the latest patch version. If a version number is composed of only the major and minor versions, bower update will update the package to the latest minor version.

Searching the Bower registry

All registered Bower components are indexed and searchable through the command line. If you don't know the exact package name of a component you wish to install, you can perform a search to retrieve a list of matching names.

Most components will have a list of keywords in their bower.json file so that you can more easily find the package without knowing the exact name. For example, you may want to install PhantomJS for headless browser testing:

$ bower search phantomjs

The list returned will include any package with phantomjs in the package name or within its keywords list:

    phantom git://github.com/ariya/phantomjs.git
    dt-phantomjs git://github.com/keesey/dt-phantomjs
    qunit-phantomjs-runner git://github.com/jonkemp/...
    parse-cookie-phantomjs git://github.com/sindresorhus/...
    highcharts-phantomjs git://github.com/pesla/highcharts-phantomjs.git
    mocha-phantomjs git://github.com/metaskills/mocha-phantomjs.git
    purescript-phantomjs git://github.com/cxfreeio/purescript-phantomjs.git

You can see from the returned list that the correct package name for PhantomJS is in fact phantom and not phantomjs. You can then proceed to install the package now that you know the correct name:

$ bower install phantom --save-dev

Now you have Bower installed and know how to manage your frontend web components and development tools, but how do you integrate them into your SPA? This is where Grunt comes in.

Grunt

Grunt is a JavaScript task runner for Node.js, and if you haven't used it before, it is perhaps the best tool you never knew you needed. You will find it useful for a myriad of tasks including CSS and JavaScript linting and minification, JavaScript template pre-compilation, LESS and SASS pre-processing, and so much more. There are indeed alternatives to Grunt, but none with as large an ecosystem of plugins (at the time of writing).

There are two components to Grunt: the Grunt CLI, and the Grunt task runner itself. The Grunt CLI allows you to run the Grunt task runner command from the command line within a directory that has Grunt installed. This allows you to have a different version of Grunt running for each project on your machine, making each app more maintainable. For more information, see gruntjs.com.

Installing the Grunt CLI

You will want to install the Grunt CLI globally, just as you did with Bower:

$ npm install -g grunt-cli

Remember that the Grunt CLI is not the Grunt task runner. It simply makes the grunt command available to you from the command line. This distinction is important, because while the grunt command will be globally available from the command line, it will always look for a local installation in the directory from which you run it.

Installing the Grunt task runner

You will install the Grunt task runner locally from the root of your app where your package.json file is located. Grunt is installed as a Node.js package:

$ npm install grunt --save-dev

Once you have Grunt installed locally, your package.json file should look like the following example:

{ 
  "name": "my-app", 
  "version": "1.0.0", 
  "author": "Philip Klauzinski", 
  "license": "MIT", 
  "devDependencies": { 
    "grunt": "^0.4.5" 
  } 
} 

You will notice a devDependencies object has been added to your package.json file, if it was not already there from a previous install.

Now that you have Grunt installed locally, let's begin installing some plugins to work with.

Installing Grunt plugins

All Grunt task plugins are Node.js packages, so they will be installed using NPM as well. There are thousands of Grunt plugins written by a multitude of authors, as Grunt is an open-source project. Every Node.js package for Grunt is prefixed with grunt in the name. The Grunt team, however, does maintain many plugins themselves. The officially maintained Grunt plugins are all prefixed with grunt-contrib, so this is how you can differentiate them if you wish to stick with only officially maintained Grunt plugins. To view and search all registered Grunt plugins, see gruntjs.com/plugins.

Since you will be writing a JavaScript SPA, let's begin by installing a JavaScript linting plugin for Grunt. Linting refers to running a program against your code to analyze it for errors and, in some cases, proper formatting. It is always a good idea to have a linting utility running to test your JavaScript code for valid syntax and formatting:

$ npm install grunt-contrib-jshint --save-dev

This will install the officially maintained Grunt plugin for JSHint and add it to the devDependencies object in your package.json file as shown in the following example:

{ 
  "name": "my-app", 
  "version": "1.0.0", 
  "author": "Philip Klauzinski", 
  "license": "MIT", 
  "devDependencies": { 
    "grunt": "^0.4.5", 
    "grunt-contrib-jshint": "^0.11.3" 
  } 
} 

JSHint is a popular tool for detecting errors and potential problems in your JavaScript code. The Grunt plugin itself will allow you to automate that process so that you can easily check your code as you develop.

Another invaluable Grunt plugin is grunt-contrib-watch. This plugin allows you to run a task which will automatically run other Grunt tasks when you add, delete, or edit files in your project that match a predefined set of rules.

$ npm install grunt-contrib-watch --save-dev

After installing the grunt-contrib-watch plugin, the devDependencies object in your package.json file should look like this:

  "devDependencies": { 
    "grunt": "^0.4.5", 
    "grunt-contrib-jshint": "^0.11.3", 
    "grunt-contrib-watch": "^0.6.1" 
  } 

Now that you have a couple of Grunt plugins installed, let's begin writing some tasks for them. In order to do that, you will first need to create a local configuration file for Grunt.

Configuring Grunt

Unlike NPM and Bower, Grunt does not provide an init command for initializing its configuration file. Instead, scaffolding tools can be used for this. Project scaffolding tools are designed to set up some basic directory structure and configuration files for a development project. Grunt maintains an official scaffolding tool called grunt-init, which is referenced on their website. The grunt-init tool must be installed separately from the grunt-cli global package and the local grunt package for any particular project. It is most useful if installed globally, so it can be used with any project.

$ npm install -g grunt-init

We will not go into further detail on grunt-init here, but if you would like to learn more, you can visit gruntjs.com/project-scaffolding.

The best way to learn about configuring Grunt is to write its configuration file by hand. The configuration for Grunt is maintained in a file called Gruntfile.js, referred to as a Gruntfile, located in the root directory of your project, along with package.json and bower.json. If you are not familiar with Node.js and its concept of modules and exports, the syntax for a Gruntfile may be a bit confusing at first. Since Node.js files run on the server and not in a browser, they do not interact in the same way that files loaded in a browser do, with respect to browser globals.

Understanding Node.js modules

In Node.js, a module is a JavaScript object defined within a file. The module name is the name of the file. For instance, if you want to declare a module named foo, you will create a file named foo.js. In order for the foo module to be accessible to another module, it must be exported. In its most basic form, a module looks something like the following example:

module.exports = { 
    // Object properties here 
};

Every module has a local exports variable that allows you to make the module accessible to others. In other words, the object module within a file refers to the current module itself, and the exports property of module makes that module available to any other module (or file).

Another way of defining a module is by exporting a function, which is of course a JavaScript object itself:

module.exports = function() { 
    // Code for the module here 
};

When you call for a Node.js module from within a file, it will first look for a core module, all of which are compiled into Node.js itself. If the name does not match a core module, it will then look for a directory named node_modules beginning from the current or root directory of your project. This directory is where all of your local NPM packages, including Grunt plugins, will be stored. If you performed the installs of grunt-contrib-jshint and grunt-contrib-watch from earlier, you will see that this directory now exists within your project.

Now that you understand a bit more about how Node.js modules work, let's create a Gruntfile.

Creating a Gruntfile

A Gruntfile uses the function form of module.exports as shown previously. This is referred to as a wrapper function. The grunt module itself is passed to the wrapper function. The grunt module will be available to your Gruntfile because you installed the grunt NPM package locally:

module.exports = function(grunt) { 
    // Grunt code here 
};

This example shows what your initial Gruntfile should look like. Now let's flesh it out some more. In order to configure Grunt and run tasks with it, you will need to access the grunt module that is passed in to your Gruntfile.

module.exports = function(grunt) { 
    'use strict'; 
    grunt.initConfig({ 
        pkg: grunt.file.readJSON('package.json') 
});
};

This basic format is what you will be working with the rest of the way. You can see here that the grunt.initConfig method is called and passed a single configuration object as a parameter. This configuration object is where all of your Grunt task code will go. The pkg property shown in this example, which is assigned the value of grunt.file.readJSON('package.json'), allows you to pass in information about your project directly from your package.json file. The use of this property will be shown in later examples.

Defining Grunt task configuration

Most Grunt tasks expect their configuration to be defined within a property of the same name as the task, which is the suffix of the package name. For example, jshint is the Grunt task name for the grunt-contrib-jshint package we previously installed:

module.exports = function(grunt) { 
    'use strict'; 
    grunt.initConfig({ 
        pkg: grunt.file.readJSON('package.json'), 
        jshint: { 
            options: { 
                curly: true, 
                eqeqeq: true, 
                eqnull: true, 
                browser: true, 
                newcap: false, 
                es3: true, 
                forin: true, 
                indent: 4, 
                unused: 'vars', 
                strict: true, 
                trailing: true, 
                quotmark: 'single', 
                latedef: true, 
                globals: { 
                    jQuery: true 
                } 
            }, 
            files: { 
                src: ['Gruntfile.js', 'js/src/*.js'] 
            } 
        } 
    }); 
}; 

Here you can see that the jshint property of the configuration object is defined and is assigned its own properties which apply to the jshint Grunt task itself. The options property defined within jshint holds the settings you wish to validate against when linting your JavaScript files. The files property defines a list of the files you wish to validate. For more information on the supported options for JSHint and what they mean, see jshint.com/docs/.

Let's now add an additional configuration for the grunt-contrib-watch plugin watch task below the jshint task configuration:

watch: { 
    jshint: { 
        files: ['js/src/*.js'], 
        tasks: ['jshint'] 
    } 
} 

Here we add an additional namespace of jshint underneath the watch task, which allows for other targets to be defined within the same configuration property and run separately if needs be. This is what is known as a multitask. Targets within a multitask can be named arbitrarily and will simply be run in the order which they are defined if the multitask is called alone. A target can be called directly as well, and doing so will ignore any of the other targets defined within the multitask's configuration:

$ grunt watch:jshint

This particular configuration for the target jshint tells the watch task that if any files matching js/src/*.js are changed, then to run the jshint task.

Now you have your first two Grunt task configurations defined within your Gruntfile, but in order to use them, we must load the Grunt tasks themselves.

Loading Grunt plugins

You have already installed the grunt-contrib-jshint plugin as a Node.js module, but in order to execute the jshint task, you must load the plugin within your Gruntfile. This is done after the grunt.initConfig call:

grunt.loadNpmTasks('grunt-contrib-jshint'); 

This is the same method call you will use to load all Grunt tasks within your Gruntfile, and any Grunt task will not be accessible without doing so. Let's do the same for grunt-contrib-watch:

grunt.loadNpmTasks('grunt-contrib-watch'); 

Your full Gruntfile should now look like this:

module.exports = function(grunt) { 
    'use strict'; 
    grunt.initConfig({ 
        pkg: grunt.file.readJSON('package.json'), 
        jshint: { 
            options: { 
                curly: true, 
                eqeqeq: true, 
                eqnull: true, 
                browser: true, 
                newcap: false, 
                es3: true, 
                forin: true, 
                indent: 4, 
                unused: 'vars', 
                strict: true, 
                trailing: true, 
                quotmark: 'single', 
                latedef: true, 
                globals: { 
                    jQuery: true 
                } 
            }, 
            files: { 
                src: ['Gruntfile.js', 'js/src/*.js'] 
            } 
        }, 
        watch: { 
            jshint: { 
                files: ['js/src/*.js'], 
                tasks: ['jshint'] 
            } 
        } 
    }); 
    grunt.loadNpmTasks('grunt-contrib-jshint'); 
    grunt.loadNpmTasks('grunt-contrib-watch'); 
}; 

Running the jshint Grunt task

Now that you have the plugin loaded, you can simply run grunt jshint from the command line to execute the task with its defined configuration. You should see the following output:

$ grunt jshint
Running "jshint:files" (jshint) task
>> 1 file lint free.
Done, without errors

This will run your JSHint linting options against the defined files, which as of now consist of only Gruntfile.js. If it looks like the example file shown and includes the call to grunt.loadNpmTasks('grunt-contrib-jshint'), then it should pass without errors.

Now let's create a new JavaScript file and intentionally include some code which will not pass the JSHint configuration so we can see how the errors are reported. First, create the js/src directory, which is defined in the files property of the jshint task:

$ mkdir -p js/src

Then create a file named app.js within this directory and place the following code in it:

var test = function() { 
    console.log('test'); 
}; 

Now run grunt jshint again from the command line. You should see the following output:

$ grunt jshint
Running "jshint:files" (jshint) task
   js/src/app.js
      2 |    console.log('test');
             ^ Missing "use strict" statement.
      1 |var test = function() {
             ^ 'test' is defined but never used.
>> 2 errors in 2 files
Warning: Task "jshint:files" failed. Use --force to continue.
Aborted due to warnings.

You will notice that two errors are reported for js/src/app.js based on the jshint task configuration options. Let's fix the errors by changing the code in app.js to the following:

var test = function() { 
    'use strict'; 
    console.log('test'); 
}; 
test(); 
 

Now if you run grunt jshint from the command line again, it will report that the files are lint free and have no errors:

$ grunt jshint
Running "jshint:files" (jshint) task
>> 2 files lint free.
Done, without errors.

Running the watch Grunt task

As mentioned earlier, when the watch task is run it will wait for changes that match the file patterns defined in its configuration and run any corresponding tasks. In this case, we configured it to run jshint when any files matching js/src/*.js are changed. Since we defined a target within the watch task called jshint, the watch task can be run in two different ways:

$ grunt watch

Running grunt watch will watch for changes matching all target configurations defined within the watch task:

$ grunt watch:jshint

Running grunt watch:jshint with the colon (:) syntax runs watch for just the file patterns matching that target configuration. In our case, only one target is defined, so let's just run grunt watch and see what happens in the console:

$ grunt watch
Running "watch" task
Waiting...

You will see that the task now shows a status of Waiting... on the command line. This indicates that the task is running to watch for matching changes within its configuration, and if any of those changes are made, it will automatically run the corresponding tasks. In our example with the jshint task, it will allow your code to automatically be linted every time you make changes to your JavaScript files and save them. If a JSHint error occurs, the console will alert you and display the error.

Let's test this by opening a text editor and changing js/src/app.js again:

var test = function() { 
    console.log('test'); 
}; 
test() 

Here, we removed the opening use strict statement and the semicolon after the call to test() at the end of the file. This should raise two JSHint errors:

>> File "js/src/app.js" changed.
Running "jshint:files" (jshint) task

   js/src/app.js
      2 |    console.log('test');
             ^ Missing "use strict" statement.
      4 |test()
               ^ Missing semicolon.
>> 2 errors in 2 files
Warning: Task "jshint:files" failed. Use --force to continue.
Aborted due to warnings.

Now let's correct these errors and return the file to the way it was before:

var test = function() { 
    'use strict'; 
    console.log('test'); 
}; 
test(); 

Press Ctrl + C from the command line at any time to abort the watch task, or any Grunt task, while it is running.

Defining the default Grunt task

Grunt allows you to define a default task which will run when you simply type grunt on the command line with no parameters. To do this, you will use the grunt.registerTask() method:

grunt.registerTask('default', ['jshint', 'watch:jshint']);

This example sets the default Grunt task to run the defined jshint task first and then the watch:jshint multitask target. You can see that the tasks passed to the default task are in an array, so you can set the default task for Grunt to run any number of tasks by simply typing grunt on the command line:

$ grunt
Running "jshint:files" (jshint) task
>> 2 files lint free.
Running "watch:jshint" (watch) task
Waiting...

From looking at the output, you can see that the jshint task was run once initially, and then watch:jshint was run to wait for additional changes to the configured file patterns.

Defining custom tasks

Grunt allows you to define your own custom tasks, in the same way that you defined the default task. In this way, you can actually write your own custom tasks directly within the Gruntfile, or you can load them from an external file, just as you did with grunt-contrib-jshint and grunt-contrib-watch.

Alias tasks

One way of defining a custom task is to simply call one or more existing tasks in the order you want them to be run:

grunt.registerTask('my-task', 'My custom task.', ['jshint']); 
 

In this example, we have simply defined a task named my-task to serve as a proxy for jshint. The second parameter is an optional description of the task, which must be a string. The third parameter, which passes an array, including only jshint in this example, must always be an array. You can also forgo the second parameter with the description and pass in your array of tasks there instead. This way of defining a task is known as an alias task.

Basic tasks

When you define custom Grunt tasks, you are not limited to only calling other tasks that exist within your configuration, but you can write JavaScript code to be called directly as a function. This is called a basic task:

grunt.registerTask('my-task', 'My custom task.', function() { 
    grunt.log.writeln('This is my custom task.'); 
}); 

In this example, we simply write a string to the command line output for the task. The output should look like this:

$ grunt my-task
Running "my-task" task
This is my custom task.

Let's expand upon this example and pass in some arguments to our basic task function, as well as access the arguments from within the function:

grunt.registerTask('my-task', 'My custom task.', function(arg1, arg2) { 
    grunt.log.writeln(this.name + ' output...'); 
    grunt.log.writeln('arg1: ' + arg1 + ', arg2: ' + arg2); 
}); 

You will notice that there is a property available to the basic task, this.name, which is simply a reference to the name of the task. In order to call a basic task from the command line and pass arguments in, you will use a colon after the task name to define each argument in succession. This syntax is just like the syntax for running a multitask target; however, in this case you are passing in arbitrary arguments:

$ grunt my-task:1:2

Running this will output the following:

Running "my-task:1:2" (my-task) task
my-task output...
arg1: 1, arg2: 2

If you do not pass in the arguments to a task that is expecting them, it will simply resolve them as undefined:

$ grunt my-task
Running "my-task" task
my-task output...
arg1: undefined, arg2: undefined
Done, without errors.

You can also call other tasks from within a custom task:

grunt.registerTask('foo', 'My custom task.', function() { 
    grunt.log.writeln('Now calling the jshint and watch tasks...'); 
    grunt.task.run('jshint', 'watch'); 
}); 

In this example, we have created a task, foo, that defines a custom function that calls the existing jshint and watch tasks:

$ grunt foo
Running "foo" task
Now calling the jshint and watch tasks...
Running "jshint:files" (jshint) task
>> 2 files lint free.
Running "watch" task
Waiting...

For more information on creating custom tasks with Grunt, see gruntjs.com/creating-tasks.

These examples of tasks only scratch the surface of what is capable with Grunt, but you should be able to glean from them the power of it and begin to think about what might be possible with Grunt tasks when building your own SPA.

Summary

Now that you have learned to set up an optimal development environment with NPM, supply it with frontend dependencies using Bower, and automate development tasks using Grunt, it's time to start learning more about building a real app. In the next chapter, we will dive into common SPA architecture design patterns, what they mean, and what is the best design pattern to choose based on the type of SPA you are building.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Build large-scale, feature-complete SPAs by leveraging widely used tools and techniques.
  • Gain a solid understanding of architecture and SPA design to build applications using the library or framework of your choice.
  • Explore the various facets of SPA development to build web apps that are fast, scalable, and easy to test.

Description

Single-page web applications—or SPAs, as they are commonly referred to—are quickly becoming the de facto standard for web app development. The fact that a major part of the app runs inside a single web page makes it very interesting and appealing. Also, the accelerated growth of browser capabilities is pushing us closer to the day when all apps will run entirely in the browser. This book will take your JavaScript development skills to the next level by teaching you to create a single-page application within a full-stack JavaScript environment. Using only JavaScript, you can go from being a front-end developer to a full-stack application developer with relative ease. You will learn to cross the boundary from front-end development to server-side development through the use of JavaScript on both ends. Use your existing knowledge of JavaScript by learning to manage a JSON document data store with MongoDB, writing a JavaScript powered REST API with Node.js and Express, and designing a front-end powered by AngularJS. This book will teach you to leverage the MEAN stack to do everything from document database design, routing REST web API requests, data-binding within views, and adding authentication and security to building a full-fledged, complex, single-page web application. In addition to building a full-stack JavaScript app, you will learn to test it with JavaScript-powered testing tools such as Mocha, Karma, and Jasmine. Finally, you will learn about deployment and scaling so that you can launch your own apps into the real world.

Who is this book for?

This book is ideal for JavaScript developers who want to build complex single-page applications in JavaScript. Some basic understanding of SPA concepts will be helpful but not essential.

What you will learn

  • Organize your development environment using the command line with NPM, Bower, and Grunt.
  • Choose an accurate design pattern for your app
  • Understand modular JavaScript programming and Node.js
  • Interact with a REST API using JavaScript and AJAX with practical examples
  • Build a single page application using the MEAN stack
  • Connect your app across popular social media platforms such as Facebook, Twitter, and LinkedIn
  • Test your app, both on the server side and in views
  • Prepare your app for the real world and deploy it to Heroku
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Oct 28, 2016
Length: 452 pages
Edition : 1st
Language : English
ISBN-13 : 9781785881640
Vendor :
Google
Languages :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Oct 28, 2016
Length: 452 pages
Edition : 1st
Language : English
ISBN-13 : 9781785881640
Vendor :
Google
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 152.97
Node.js  Design Patterns
$54.99
Mastering JavaScript Single Page Application Development
$48.99
MEAN Web Development
$48.99
Total $ 152.97 Stars icon

Table of Contents

14 Chapters
1. Getting Organized with NPM, Bower, and Grunt Chevron down icon Chevron up icon
2. Model-View-Whatever Chevron down icon Chevron up icon
3. SPA Essentials – Creating the Ideal Application Environment Chevron down icon Chevron up icon
4. REST is Best – Interacting with the Server Side of Your App Chevron down icon Chevron up icon
5. Its All About the View Chevron down icon Chevron up icon
6. Data Binding, and Why You Should Embrace It Chevron down icon Chevron up icon
7. Leveraging the MEAN Stack Chevron down icon Chevron up icon
8. Managing Data Using MongoDB Chevron down icon Chevron up icon
9. Handling Web Requests with Express Chevron down icon Chevron up icon
10. Displaying Views Chevron down icon Chevron up icon
11. Adding Security and Authentication Chevron down icon Chevron up icon
12. Connecting the App to Social Media Chevron down icon Chevron up icon
13. Testing with Mocha, Karma, and More Chevron down icon Chevron up icon
14. Deploying and Scaling the SPA Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
(2 Ratings)
5 star 50%
4 star 0%
3 star 50%
2 star 0%
1 star 0%
Kerridee Aug 04, 2020
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Brought JavaScript to life for me... easy to understand!
Amazon Verified review Amazon
AW 65843 Dec 22, 2016
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
The first third of the book is lucid and well-written. Early chapters walk you through setting up your workspace to write JavaScript-based web apps including walking you through the use of tools like npm, bower and grunt. There's a very nice chapter on the history of MCV -> MVW architectures. It's a good read.About 1/3 of the way through the book it seems like the authors gave up on the editing process. At that point there is still some good content but it feels like a rough draft. There's a lot of repetition after that -- for example I don't know how many times they tell you what Mongo is, why they like it, and that MongoDB uses BSON, and what BSON is; or how many times they tell you what Angular and Express are. I hope the author will go back, finish the second 1/2 to 2/3 of the book, and republish.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the digital copy I get with my Print order? Chevron down icon Chevron up icon

When you buy any Print edition of our Books, you can redeem (for free) the eBook edition of the Print Book you’ve purchased. This gives you instant access to your book when you make an order via PDF, EPUB or our online Reader experience.

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela