Developing with JavaScript - Full Stack Web Development with Backbone.js (2014)

Full Stack Web Development with Backbone.js (2014)

Appendix A. Developing with JavaScript

For client-side application development, a good understanding of JavaScript is necessary. JavaScript is a dynamic, object-oriented language. For an in-depth background, you might want to refer to some of the books listed in the Preface. This appendix provides a short overview on getting started with Node.js and a short refresher on the role of Underscore and jQuery.

Installing Node

Node.js is based on Google’s V8 library, which is written in C++. Node.js runs on all major operating systems.

Starting with Mac OS, an easy approach to install Node.js is by using Homebrew. Homebrew is a package manager for Mac OS (see http://brew.sh/ for basic installation instructions).

Once Homebrew is installed, you can run the following:

$ brew upgrade

$ brew install node

Alternatively, you can visit the download page.

Here, you also find the Node versions for Windows and source code packages.

If you are running an Ubuntu or Debian flavor of Linux, you can install Node.js with:

sudo apt-get install build-essential libssl-dev curl git-core

sudo apt-get install nodejs

If you need to run different versions of Node.js (e.g., if you are dealing with constraints in production), you might want to have a look at NVM. With NVM, you can easily switch between versions of Node.js.

Getting Functional with Underscore.js

Because JavaScript in its old standard didn’t have helpers for dealing with enumerators, Underscore.js was born. Underscore.js provides many nifty JavaScript shortcuts and is also a core dependency of Backbone.js. Underscore.js derives many ideas from functional programming.

In functional programming, we can easily extract meta information from data structures, or chain operations on data structures independent from the data itself. This flexibility is achieved by passing functions as arguments to other functions, often under the concept of higher-level functions.

The main goal of the Underscore.js library is to provide a set of abstractions to transform and aggregate data structures. Underscore.js comes with excellent documentation.

After loading the library, we obtain a helper to the global namespace of the browser window. To get a feeling of what the shortcut can do, we can look at some examples.

TIP

Due to the success of Underscore.js, there are a number of derivatives from the original library. First, in underscore-contrib, a lot more ideas from functional programming are made possible in JavaScript. Another variation can be found in underscore.string. This library provides a number of default string transformations Last, there is underscore-cli, which provides helpers to transform JSON from the command line.

Collections and Arrays

Some of the most important helpers from Underscore.js are improvements in dealing with collections. The helpers that are provided from Underscore.js will look familiar to Ruby developers. We need to load underscore into the console.

Let’s build a movies list:

var midnight_in_paris = {title: "Midnight in Paris"};

var indiana_jones = {title: "Indiana Jones"};

var movies = [midnight_in_paris, indiana_jones]

var show = function(movie) { console.log(movie.title); }

_.each(movies, show); // 1

For the _.each() helper, we pass in an array and a function that operates on the members of the list. The output of (1) will then look like:

"Midnight in Paris"

"Indiana Jones"

Next, Underscore.js helps in bringing objects into a new form. For example, with _.map(), we can extract a list of titles from a movies list:

var movie_titles = _.map(movies, function(movie) {

return movie.title;

});

console.log(movie_titles);

With _.reduce(), we can, for example, sum up numbers in a list:

_.reduce(movies, function(actor_no, movie) {

return actor_no + movie.actors.length

},

0);

With Underscore.js, it also is easy to make the union of two sets:

var allGenres = _.union(midnight_in_paris.genres, indiana_jones.genres); // (3)

console.log(allGenres);

Functions

We mentioned the role of context in JavaScript a few times already. Because functions can be executed from different contexts, it is often helpful to explicitly bind a context to a function. One option to do this can be Underscore’s bind and bindAll functions. Starting with ECMAScript 5, you can also use a new function called Function.prototype.bind that can natively enforce a context.

The idea of bind can be seen as follows:

announceMovie = _.bind(announceMovie, {title: 'The Artist'});

announceMovie()

Coming next: The Artist

So, we can call a function, without passing an argument or referencing the outer context. These kinds of shortcuts allow us to encapsulate code and data (e.g., to be used in callbacks).

For example, if title changes or is undefined for a certain function context, the method announceMovie still uses the object that was bound. So:

name = 'Taxi Driver'

setTimeout(announceMovie, 1000);

still results in:

Coming next: The Artist

Another helper from Underscore is invoke:

var complete = _.invoke([movies, genres], 'fetch', {async: false});

With this, functions can be invoked on a list of objects, such as the array [movies, genres] discussed earlier.

Objects

From the perspective of code organization, it is often necessary to share functions across multiple objects. This would be difficult if we could only use JavaScript prototype inheritance. Underscore.js provides some other ways to customize the interfaces of objects. For example, there isextend, which allows us to copy properties from one object onto another:

var movieReview = _.extend(midnight_in_paris,

{ reviewer: 'anne', description: '...'});

console.log(movieReview);

We now have the properties from the movie midnight_in_paris copied onto the movieReview object.

Apart from new ways to customize interfaces of objects, Underscore.js provides some easy ways to introspect objects.

For example, values() would just return the values of object properties:

var summary = _.values(midnight_in_paris);

console.log(summary);

Utility

The last group of helpers from Underscore.js are just plain utility functions.

First, there are helpers to render objects within templates. The general idea is the following:

var welcome = _.template("Welcome, <%= name %>!");

console.log(welcome({name: 'patrick'}));

Welcome, patrick!

jQuery Basics

Backbone.js applications will require a library to modify the nodes in the DOM and to work with Ajax requests out of the browser. Therefore, Backbone.js depends on a library like jQuery or Zepto.

For a basic understanding of jQuery, check out jQuery Cookbook by Cody Lindley (O’Reilly, 2009) or Learning from jQuery by Callum Macrae (O’Reilly, 2013).

The short review that follows is for making the jump from server-side application development to client-side easier. In essence, jQuery is a wrapper around the DOM, and we start the discussion there.

Selecting Elements

To understand the purpose of jQuery, let’s look at what web browsers do. Web browsers parse HTML into the so-called Document Object Model (DOM). The DOM is a browser’s internal representation of HTML and is made up of nodes. After the HTML is parsed into nodes, the nodes are fed into the browser’s layout engine before a web page is displayed to the user.

There are different types of DOM nodes to represent HTML tags. For example, let’s look at the following HTML construct:

<div id="movie" class="selected">The Artist</div>

Here, an element with attributes id and class encloses a text element. The nodes could be selected with JavaScript by using:

document.getElementById('movie')

document.getElementByTag('div')

Without jQuery, walking through a list of nodes or selecting a node from a relative position would be not so easy. With jQuery, selecting nodes can be done:

$('#movie')

$('#movies article:last')

Another important syntax for selecting a node is matching attributes of tags, like:

$('article[data-id=1]')

This matches article nodes with the attribute data-id set to 1.

There is a small difference between selecting DOM nodes with pure JavaScript or with jQuery: with pure JavaScript, we merely obtain a representation of a node in JavaScript. With jQuery, we actually obtain a jQuery wrapper around a node providing us again with jQuery functionality. A jQuery wrapped enables:

Collection helpers

When we select nodes from a list. for example, we can directly operate on the list items. For example, we can use $(#movies div).hide() to hide all movies instead of looping over the nodes manually.

Chaining methods

Wrapping nodes with jQuery allows us to chain operations on a node. For example, we can toggle a CSS class and change the text value all at once:

$("#movies").first().css('background', 'green').text('a test')

Creating DOM nodes is also easier with jQuery. Instead of writing:

var movie = document.createElement('div');

movie.innerHTML = 'The Artist';

we can simply use the following:

var movie = $('<article>The Artist</article>');

Modifying the content of DOM nodes can be done with jQuery as follows:

movie.html('The Piano');

or:

movie.text('Taxi Driver');

For performance reasons, it is important to realize that html also does syntax checking operations and can be slower than simply calling the value of the innerHTML property.

Working with Events

Basically, an event consists of an event type and a node in the DOM where the event occurred. Handling events is one of the most complicated aspects of programming a web browser with JavaScript. It’s not just that various browsers have slightly different ways to attach event handlers to nodes in the DOM, but also that removing these handlers makes the life of a programmer difficult at times.

Without jQuery, we can attach an event handler directly on an element with:

var element = document.getElementById('movie');

element.onclick = function() {

// ... process the click

}

or by registering an event listener on a node with:

document.addEventListener(node, handler);

In jQuery, there are is a short syntax for adding handlers to certain event types on nodes. For example, for a click event, you can use:

$('#movie').on('click', function(ev) {

// ... process the click

}

A common problem with events is event bubbling. Multiple nodes will capture an event if events are not prevented to pass from children nodes to their parents. Event bubbling can be prevented with jQuery with:

event.preventDefault();

Also, when new nodes are created, it is a common mistake to duplicate unwanted event handlers (e.g., for submitting a form). It is important to clean up events from “old” nodes before new nodes are inserted.

Ajax

For quite some years, loading new content with JavaScript into the browser was rather difficult. Things changed when the XMLHttpRequest object was introduced into the DOM.

The name XMLHttpRequest is confusing at first, because it can load any content (not only XML) and it can also talk HTTPS (not only HTTP). In order to send an HTTP request from JavaScript, we must set the request type, the URL, the header, and the request parameters on aXMLHttpRequest object. We then wait for a response event and parse the received data accordingly.

By using jQuery, we obtain some syntactic sugar around this process and a uniform behavior across most browsers. Although Backbone.js comes with a wrapper for Ajax, it is instructive to look at the jQuery Ajax API.

Let’s look at how to load additional content with the jQuery Ajax API. For example, we might have a select box where we want to load details of the selected movie. We could do this as follows:

<html>

<head>

<script src="/js/libs/jquery/jquery.js"></script>

<script>

$(document).ready(function() {

// ... here comes the Ajax magic

});

</script>

</head>

<body>

<form action="#" id="movies">

<p>Select a movie:</p>

<select name="title" id="movielist" size="1">

<option data-id='1'>The Artist</option>

<option data-id='2'>Taxi Driver</option>

<option data-id='3'>La Dolce Vita</option>

</select>

</form>

<div id="movieDetails">

</div>

</body>

</html>

We can detect a change of the selected movie with a simple event handler attached to the change event. We then load the movie details with Ajax as follows:

$(function(){

$('#movielist').change(function() {

var title = $(this).val();

var id = $(this).data('id');

$('#movieDetails').load('movies.json', { title: title });

}).change();

});