Understanding and Implementing Information Management Concepts and Techniques

BackboneJS Frontend Web Apps with Cloud Storage Tutorial Part 1: Building a Minimal App in Seven Steps

Building a frontend web application with minimal effort in seven steps, using the BackboneJS framework and the cloud storage service Parse.com

Gerd Wagner

Warning: This tutorial may still contain errors and may still be incomplete in certain respects. Please report any issue to Gerd Wagner at G.Wagner@b-tu.de.

This tutorial is also available in the following formats: PDF. See also the project page, or run the example app from our server, or download it as a ZIP archive file.

This tutorial article, along with any associated source code, is licensed under The Code Project Open License (CPOL), implying that the associated code is provided "as-is", can be modified to create derivative works, can be redistributed, and can be used in commercial applications, but the article must not be distributed or republished without the author's consent.

2015-03-11

Revision History
Revision 0.220150311gw
update Parse API version and screenshot, replace input with output for ISBN in Update Book
Revision 0.120140601gw
create first version

Table of Contents

Foreword
1. A Quick Tour of the Foundations of Web Apps
1. The World Wide Web (WWW)
2. HTML and XML
2.1. XML documents
2.2. Unicode and UTF-8
2.3. XML namespaces
2.4. Correct XML documents
2.5. The evolution of HTML
2.6. HTML forms
3. JavaScript
3.1. Types and data literals in JavaScript
3.2. Variable scope
3.3. Strict Mode
3.4. Different kinds of objects
3.5. Array lists
3.6. Maps
3.7. JavaScript supports four types of basic data structures
3.8. Defining and using classes
3.9. JavaScript as an object-oriented language
3.10. Further reading about JavaScript
2. BackboneJS/Parse Web Apps Tutorial Part 1
1. Using the Cloud Storage Service of Parse.com with BackboneJS
1.1. Getting Started with Parse
1.2. The BackboneJS/Parse Model Layer
2. Building a Minimal BackboneJS/Parse App in Seven Steps
2.1. Step 1 - Set up the Folder Structure
2.2. Step 2 - Write the Model Code
2.3. Step 3 - Initialize the App
2.4. Step 4 - Develop the Use Case "List Objects"
2.5. Step 5 - Develop the Use Case "Create Object"
2.6. Step 6 - Develop the Use Case "Update Object"
2.7. Step 7 - Develop the Use Case "Delete Object"
3. Run the App and Get the Code
4. Points of Attention

List of Figures

2.1. The object type Book.
2.2. Looking up your app's data with the web user interface of Parse.

List of Tables

1.1. Constructor-based versus factory-based classes
2.1. Table 2: A collection of book objects represented as a Parse table

Foreword

This tutorial is Part 1 of a series of five tutorials about engineering a frontend web application with BackboneJS using the cloud storage service provided by Parse. It shows how to build such an app with minimal effort, only using a minimal number of BackboneJS functions and not any additional third-party library. A frontend web app can be provided by any web server, but it is executed on the user's computer device, and not on the remote web server. Typically, but not necessarily, a frontend web app is a single-user application, which is not shared with other users.

The minimal version of a BackboneJS/Parse data management application discussed in this tutorial only includes a minimum of the overall functionality required for a complete app. It takes care of only one object type ("books") and supports the four standard data management operations (Create/Read/Update/Delete), but it needs to be enhanced by styling the user interface with CSS rules, and by adding further important parts of the app's overall functionality:

  • Part 2: Handling constraint validation.

  • Part 3: Managing unidirectional associations between object types, such as assigning authors to their books.

  • Part 4: Managing bidirectional associations between object types, such as assigning authors to their books and books to their author.

  • Part 5: Handling subtype (inheritance) relationships between object types.

Chapter 1. A Quick Tour of the Foundations of Web Apps

If you are already familiar with HTML, XML and JavaScript, you can skip this chapter and immediately start developing a minimal front-end web application with JavaScript in the following chapter.

1. The World Wide Web (WWW)

After the Internet had been established in the 1980'ies, Tim Berners-Lee developed the idea and the first infrastructure components of the WWW in 1989 at the European research institution CERN in Geneva, Switzerland. The WWW (or, simply, "the web") is based on

  • the basic Internet technologies TCP/IP and DNS,

  • the Hypertext Transfer Protocol (HTTP),

  • the Hypertext Markup Language (HTML) as well as the Extensible Markup Language (XML), and

  • web server programs, acting as HTTP servers, as well as web 'user agents' (such as browsers), acting as HTTP clients.

2. HTML and XML

HTML allows to mark up (or describe) the structure of a human-readable web document or web user interface, while XML allows to mark up the structure of all kinds of documents, data files and messages, whether they are human-readable or not. HTML can be based on XML.

2.1. XML documents

XML provides a syntax for expressing structured information in the form of an XML document with elements and their attributes. The specific elements and attributes used in an XML document can come from any vocabulary, such as public standards or your own user-defined XML format. XML is used for specifying

  • document formats, such as XHTML5, the Scalable Vector Graphics (SVG) format or the DocBook format,

  • data interchange file formats, such as the Mathematical Markup Language (MathML) or the Universal Business Language (UBL),

  • message formats, such as the web service message format SOAP

2.2. Unicode and UTF-8

XML is based on Unicode, which is a platform-independent character set that includes almost all characters from most of the world's script languages including Hindi, Burmese and Gaelic. Each character is assigned a unique integer code in the range between 0 and 1,114,111. For example, the Greek letter π has the code 960, so it can be inserted in an XML document as π (using the XML entity syntax).

Unicode includes legacy character sets like ASCII and ISO-8859-1 (Latin-1) as subsets.

The default encoding of an XML document is UTF-8, which uses only a single byte for ASCII characters, but three bytes for less common characters.

Almost all Unicode characters are legal in a well-formed XML document. Illegal characters are the control characters with code 0 through 31, except for the carriage return, line feed and tab. It is therefore dangerous to copy text from another (non-XML) text to an XML document (often, the form feed character creates a problem).

2.3. XML namespaces

Generally, namespaces help to avoid name conflicts. They allow to reuse the same (local) name in different namespace contexts.

XML namespaces are identified with the help of a namespace URI (such as the SVG namespace URI "http://www.w3.org/2000/svg"), which is associated with a namespace prefix (such as "svg"). Such a namespace represents a collection of names, both for elements and attributes, and allows namespace-qualified names of the form prefix:name (such as "svg:circle" as a namespace-qualified name for SVG circle elements).

A default namespace is declared in the start tag of an element in the following way:

<html xmlns="http://www.w3.org/1999/xhtml">

This example shows the start tag of the HTML root element, in which the XHTML namespace is declared as the default namespace.

The following example shows a namespace declaration for the SVG namespace:

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
   ...
  </head>
  <body>
    <figure>
      <figcaption>Figure 1: A blue circle</figcaption>
      <svg:svg xmlns:svg="http://www.w3.org/2000/svg">
        <svg:circle cx="100" cy="100" r="50" fill="blue"/>
      </svg:svg>
    </figure>
  </body>
</html>

2.4. Correct XML documents

XML defines two syntactic correctness criteria. An XML document must be well-formed, and if it is based on a grammar (or schema), then it must also be valid against that grammar.

An XML document is called well-formed, if it satisfies the following syntactic conditions:

  1. There must be exactly one root element.

  2. Each element has a start tag and an end tag; however, empty elements can be closed as <phone/> instead of <phone></phone>.

  3. Tags don't overlap, e.g. we cannot have

    <author><name>Lee Hong</author></name>
  4. Attribute names are unique within the scope of an element, e.g. the following code is not correct:

    <attachment file="lecture2.html" file="lecture3.html"/>

An XML document is called valid against a particular grammar (such as a DTD or an XML Schema), if

  1. it is well-formed,

  2. and it respects the grammar.

2.5. The evolution of HTML

The World-Wide Web Committee (W3C) has developed the following important versions of HTML:

  • HTML4 as an SGML-based language (in 1997),

  • XHTML 1 as an XML-based version of HTML4 (in 2000),

  • (X)HTML5 in cooperation (and competition) with the WHAT working group led by Ian Hickson and supported by browser vendors (in 2014).

HTML was originally designed as a structure description language, and not as a presentation description language. But HTML4 has a lot of purely presentational elements such as font. XHTML has been taking HTML back to its roots, dropping presentational elements and defining a simple and clear syntax, in support of the goals of

  • device independence,

  • accessibility, and

  • usability.

We adopt the symbolic equation

HTML = HTML5 = XHTML5

stating that when we say "HTML" or "HTML5", we actually mean XHTML5

because we prefer the clear syntax of XML documents over the liberal and confusing HTML4-style syntax that is also allowed by HTML5.

The following simple example shows the basic code template to be used for any HTML document:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <meta charset="UTF-8" />
  <title>XHTML5 Template Example</title>
 </head>
 <body>
  <h1>XHTML5 Template Example</h1>
  <section><h1>First Section Title</h1>
   ...
  </section>
 </body>
</html>

2.6. HTML forms

For user-interactive web applications, the web browser needs to render a user interface. The traditional metaphor for a software application's user interface is that of a form. The special elements for data input, data output and form actions are called form controls. An HTML form is a section of a document consisting of block elements that contain controls and labels on those controls.

Users complete a form by entering text into input fields and by selecting items from choice controls. A completed form is submitted with the help of a submit button. When a user submits a form, it is sent to a web server either with the HTTP GET method or with the HTTP POST method. The standard encoding for the submission is called URL-encoded. It is represented by the Internet media type application/x-www-form-urlencoded. In this encoding, spaces become plus signs, and any other reserved characters become encoded as a percent sign and hexadecimal digits, as defined in RFC 1738.

Each control has both an initial value and a current value, both of which are strings. The initial value is specified with the control element's value attribute, except for the initial value of a textarea element, which is given by its initial contents. The control's current value is first set to the initial value. Thereafter, the control's current value may be modified through user interaction or scripts. When a form is submitted for processing, some controls have their name paired with their current value and these pairs are submitted with the form.

Labels are associated with a control by including the control as a subelement of a label element ("implicit labels"), or by giving the control an id value and referencing this id in the for attribute of the label element ("explicit labels"). It seems that implicit labels are (in 2015) still not widely supported by CSS libraries and assistive technologies. Therefore, explicit labels may be preferable, despite the fact that they imply quite some overhead by requiring a reference/identifier pair for every labeled HTML form field.

In the simple user interfaces of our "Getting Started" applications, we only need three types of form controls:

  1. single line input fields created with an <input name="..." /> element,

  2. push buttons created with a <button type="button">...</button> element, and

  3. dropdown selection lists created with a select element of the following form:

    <select name="...">
      <option value="value1"> option1 </option>
      <option value="value2"> option2 </option>
      ...
    </select>

An example of an HTML form with implicit labels for creating such a user interface is

<form id="Book">
  <p><label>ISBN: <input name="isbn" /></label></p>
  <p><label>Title: <input name="title" /></label></p>
  <p><label>Year: <input name="year" /></label></p>
  <p><button type="button" id="saveButton">Save</button></p>
</form>

In an HTML-form-based user interface (UI), we have a correspondence between the different kinds of properties defined in the model classes of an app and the form controls used for the input and output of their values. We have to distinguish between various kinds of model class attributes, which are typically mapped to various kinds of input fields. This mapping is also called data binding.

In general, an attribute of a model class can always be represented in the UI by a plain input control (with the default setting type="text"), no matter which datatype has been defined as the range of the attribute in the model class. However, in special cases, other types of input controls (for instance, type="date"), or other controls, may be used. For instance, if the attribute's range is an enumeration, a select control or, if the number of possible choices is small enough (say, less than 8), a radio button group can be used.

3. JavaScript

This section provides a brief overview of JavaScript, assuming that the reader is already familiar with basic programming concepts and has some experience with programming, for instance, in PHP, Java or C#.

JavaScript is an object-oriented programming language that can be used for

  1. Enriching a web page by

    • generating browser-specific HTML content or CSS styling,

    • inserting dynamic HTML content,

    • producing special audio-visual effects (animations).

  2. Enriching a web user interface by

    • implementing advanced user interface components,

    • validating user input on the client side,

    • automatically pre-filling certain form fields.

  3. Implementing a front-end web application with local data storage.

  4. Implementing a front-end component for a distributed web application with remote data storage managed by a back-end component (server-side program).

3.1. Types and data literals in JavaScript

JavaScript has three primitive data types: string, number and boolean. There are three reference types: object, array and function. Arrays and functions are just special kinds of objects. Types are not declared and not checked since a JavaScript program is not compiled. Type conversion (casting) is performed automatically.

The value of a variable may be

  • a data value: either a string, a number, or a boolean,

  • an object reference: either referencing an ordinary object, or an array, or a function,

  • one of the following two special data values: undefined or null.

All numeric data values are represented in 64-bit floating point format with an optional exponent (like in the numeric data literal 3.1e10). There is no explicit type distinction between integers and floating point numbers. For making sure that a numeric value is an integer, or that a string representing a number is converted to an integer, one has to apply the predefined function parseInt. If a numeric expression cannot be evaluated to a number, its value is set to NaN ("not a number").

Like in Java, there are two pre-defined Boolean data literals, true and false, and the Boolean operator symbols are the exclamation mark ! for NOT, the double ampersand && for AND, and the double bar || for OR. When a non-Boolean value is used in a condition, or as an operand of a Boolean expression, it is converted into a Boolean value according to the following rules. The empty string, the (numerical) data literal 0, as well as undefined and null, are mapped to false, and all other values are mapped to true.

For equality and inequality testing, always use the triple equality symbol === and !== instead of the double equality symbol == and !=. Otherwise, for instance, the number 2 would be the same as the string "2", since the condition (2 == "2") evaluates to true in JavaScript.

3.2. Variable scope

In the current version of JavaScript, ECMAScript 5, there are only two kinds of scope for variables: the global scope (with window as the context object) and function scope, but no block scope. Consequently, declaring a variable within a block is confusing and should be avoided. For instance, although this is a frequently used pattern, even by experienced JavaScript programmers, it is a pitfall to declare the counter variable of a for loop in the loop, as in

function foo() {
  for (var i=0; i < 10; i++) {
    ...  // do something with i
  }
}

Instead, and this is exactly how JavaScript is interpreting this code, we should write:

function foo() {
  var i=0;
  for (i=0; i < 10; i++) {
    ...  // do something with i
  }
}

All variables should be declared at the beginning of a function. Only in the next version of JavaScript, ECMAScript 6, block scope will be supported by means of a new form of variable declaration with the keyword let.

3.3. Strict Mode

Starting from ECMAScript 5, we can use strict mode for getting more runtime error checking. For instance, in strict mode, all variables must be declared. An assignment to an undeclared variable throws an exception.

We can turn strict mode on by typing the following statement as the first line in a JavaScript file or inside a <script> element:

'use strict';

It is generally recommended that you use strict mode, except your code depends on libraries that are incompatible with strict mode.

3.4. Different kinds of objects

JavaScript objects are different from classical OO/UML objects. In particular, they need not instantiate a class. And they can have their own (instance-level) methods in the form of method slots, so they do not only have (ordinary) property slots, but also method slots. In addition they may also have key-value slots. So, they may have three different kinds of slots, while classical objects (called "instance specifications" in UML) only have property slots.

A JavaScript object is essentially a set of name-value-pairs, also called slots, where names can be property names, function names or keys of a map. Objects can be created in an ad-hoc manner, using JavaScript's object literal notation (JSON), without instantiating a class:

var person1 = { lastName:"Smith", firstName:"Tom"};
var o1 = {};  // an empty object with no slots

Whenever the name in a slot is an admissible JavaScript identifier, the slot may be either a property slot, a method slot or a key-value slot. Otherwise, if the name is some other type of string (in particular when it contains any blank space), then the slot represents a key-value slot, which is a map element, as explained below.

The name in a property slot may denote either

  1. a data-valued property, in which case the value is a data value or, more generally, a data-valued expression;

    or

  2. an object-valued property, in which case the value is an object reference or, more generally, an object expression.

The name in a method slot denotes a JavaScript function (better called method), and its value is a function definition text.

Object properties can be accessed in two ways:

  1. Using the dot notation (like in C++/Java):

    person1.lastName = "Smith"
  2. Using a map notation:

    person1["lastName"] = "Smith"

JavaScript objects can be used in many different ways for different purposes. Here are five different use cases for, or possible meanings of, JavaScript objects:

  1. A record is a set of property slots like, for instance,

    var myRecord = {firstName:"Tom", lastName:"Smith", age:26}
  2. A map (or 'associative array') supports look-ups of values based on keys like, for instance,

    var numeral2number = {"one":"1", "two":"2", "three":"3"}

    which associates the value "1" with the key "one", "2" with "two", etc. A key need not be a valid JavaScript identifier, but can be any kind of string (e.g. it may contain blank spaces).

  3. An untyped object does not instantiate a class. It may have property slots and function slots like, for instance,

    var person1 = { 
      lastName: "Smith", 
      firstName: "Tom",
      getInitials: function () {
        return this.firstName.charAt(0) + this.lastName.charAt(0); 
      }  
    };
  4. A namespace may be defined in the form of an untyped object referenced by a global object variable, the name of which represents a namespace prefix. For instance, the following object variable provides the main namespace of an application based on the Model-View-Controller (MVC) architecture paradigm where we have three subnamespaces corresponding to the three parts of an MVC application:

    var myApp = { model:{}, view:{}, ctrl:{} };
  5. A typed object o instantiates a class that is defined either by a JavaScript constructor function C or by a factory object F. See ???

3.5. Array lists

A JavaScript array represents, in fact, the logical data structure of an array list, which is a list where each list item can be accessed via an index number (like the elements of an array). Using the term 'array' without saying 'JavaScript array' creates a terminological ambiguity. But for simplicity, we will sometimes just say 'array' instead of 'JavaScript array'.

A variable may be initialized with a JavaScript array literal:

var a = [1,2,3];

Because they are array lists, JavaScript arrays can grow dynamically: it is possible to use indexes that are greater than the length of the array. For instance, after the array variable initialization above, the array held by the variable a has the length 3, but still we can assign a fifth array element like in

a[4] = 7;

The contents of an array a are processed with the help of a standard for loop with a counter variable counting from the first array index 0 to the last array index, which is a.length-1:

for (i=0; i < a.length; i++) { ...}

Since arrays are special types of objects, we sometimes need a method for finding out if a variable represents an array. We can test, if a variable a represents an array by applying the predefined datatype predicate isArray as in Array.isArray( a).

For adding a new element to an array, we append it to the array using the push operation as in:

a.push( newElement);

For deleting an element at position i from an array a, we use the pre-defined array method splice as in:

a.splice( i, 1);

For searching a value v in an array a, we can use the pre-defined array method indexOf, which returns the position, if found, or -1, otherwise, as in:

if (a.indexOf(v) > -1)  ...

For looping over an array a, we have two options: for loops or the array method forEach. In any case, we can use a for loop, as in the following example:

var i=0;
for (i=0; i < a.length; i++) {
  console.log( a[i]);
}

If a is sufficiently small (say, it does not contain more than a few hundred elements), we can use the pre-defined array method forEach, as in the following example, where the parameter elem iteratively assumes each element of the array as its value:

a.forEach( function (elem) {
  console.log( elem);
}) 

For cloning an array a, we can use the array function slice in the following way:

var clone = a.slice(0);

3.6. Maps

A map (also called 'hash map' or 'associative array') provides a mapping from keys to their associated values. The keys of a map may be string literals that include blank spaces like in:

var myTranslation = { 
    "my house": "mein Haus", 
    "my boat": "mein Boot", 
    "my horse": "mein Pferd"
}

A map is processed with the help of a special loop where we loop over all keys of the map using the pre-defined function Object.keys(m), which returns an array of all keys of a map m. For instance,

var i=0, key="", keys=[];
keys = Object.keys( myTranslation);
for (i=0; i < keys.length; i++) {
  key = keys[i];
  alert('The translation of '+ key +' is '+ myTranslation[key]);
}

For adding a new entry to a map, we associate the new value with its key as in:

myTranslation["my car"] = "mein Auto";

For deleting an entry from a map, we can use the pre-defined delete operator as in:

delete myTranslation["my boat"];

For searching in a map if it contains an entry for a certain key, such as for testing if the translation map contains an entry for "my bike" we can check the following:

if (myTranslation["my bike"])  ...

For looping over a map m, we first convert it to an array of its keys with the help of the predefined Object.keys method, and then we can use either a for loop or the forEach method. The following example shows how to loop with for:

var i=0, key="", keys=[];
keys = Object.keys( m);
for (i=0; i < keys.length; i++) {
  key = keys[i];
  console.log( m[key]);
}

Again, if m is sufficiently small, we can use the forEach method, as in the following example:

Object.keys( m).forEach( function (key) {
  console.log( m[key]);
}) 

Notice that using the forEach method is more concise.

3.7. JavaScript supports four types of basic data structures

In summary, the four types of basic data structures supported are:

  1. array lists, such as ["one","two","three"], which are special JS objects called 'arrays', but since they are dynamic, they are rather array lists as defined in the Java programming language.

  2. maps, which are also special JS objects, such as {"one":1,"two":2,"three":3}, as discussed above,

  3. records, which are special JS objects, such as {firstName:"Tom",lastName:"Smith"}, as discussed above,

  4. JSON tables, which are special maps where the values are entity records (with a primary key slot), and the keys are the primary keys of these entity records.

Notice that our distinction between maps, records and JSON tables is a purely conceptual distinction, and not a syntactical one. For a JavaScript engine, both {firstName:"Tom",lastName:"Smith"} and {"one":1,"two":2,"three":3} are just objects. But conceptually, {firstName:"Tom",lastName:"Smith"} is a record because firstName and lastName are intended to denote properties or fields, while {"one":1,"two":2,"three":3} is a map because "one" and "two" are not intended to denote properties/fields, but are just arbitrary string values used as keys for a map.

Making such conceptual distinctions helps to better understand the options offered by JavaScript.

3.8. Defining and using classes

The concept of a class is fundamental in object-oriented programming. Objects instantiate (or are classified by) a class. A class defines the properties and methods for the objects that instantiate it. Having a class concept is essential for being able to implement a data model in the form of model classes. However, classes and their inheritance/extension mechanism are over-used in classical OO languages, such as in Java, where all code has to live in the context of a class. This is not the case in JavaScript where we have the freedom to use classes for implementing model classes only, while keeping method libraries in namespace objects.

There is no explicit class concept in JavaScript. However, classes can be defined in two ways:

  1. In the form of a constructor function that allows to create new instances of the class with the help of the new operator. This is the classical approach recommended in the Mozilla JavaScript documents.

  2. In the form of a factory object that uses the predefined Object.create method for creating new instances of the class. In this approach, the constructor-based inheritance mechanism has to be replaced by another mechanism. Eric Elliott has argued that factory-based classes are a viable alternative to constructor-based classes in JavaScript (in fact, he even condemns the use of classical inheritance and constructor-based classes, throwing out the baby with the bath water).

Since we often need to define class hierarchies, and not just single classes, these two alternative approaches cannot be mixed within the same class hierarchy, and we have to make a choice whenever we build an app. Their pros and cons are summarized in Table 1.1.

Table 1.1. Constructor-based versus factory-based classes

Pros Cons
Constructor-based classes Higher performance object creation
  1. Do not allow to declare properties

  2. Incompatible with object pools

  3. Do not support multiple inheritance

  4. Do not support multiple classification

Factory-based classes
  1. Allow property declarations (with a property label, a range and many other constraints)

  2. Can support object pools

  3. Can support multiple inheritance

  4. Can support multiple classification

Lower performance object creation

3.8.1. Constructor-based classes

A constructor-based class can be defined in two or three steps. First define the constructor function that defines the properties of the class and assigns them the values of the constructor parameters:

function Person( first, last) {
  this.firstName = first; 
  this.lastName = last; 
}

Next, define the instance-level methods of the class as method slots of the object property prototype of the constructor:

Person.prototype.getInitials = function () {
  return this.firstName.charAt(0) + this.lastName.charAt(0); 
}

Finally, class-level ("static") methods can be defined as method slots of the constructor, as in

Person.checkName = function (n) {
  ... 
}

An instance of such a constructor-based class is created by applying the new operator to the constructor function and providing suitable arguments for the constructor parameters:

var pers1 = new Person("Tom","Smith");

The method getInitials is invoked on the object pers1 of type Person by using the 'dot notation':

alert("The initials of the person are: " + pers1.getInitials());

When a typed object o is created with o = new C(...), where C references a named function with name "C", the type (or class) name of o can be retrieved with the introspective expression o.constructor.name. which returns "C" (however the function name property is not yet supported by Internet Explorer up to the current version 11).

In this approach, an inheritance mechanism is provided via the predefined prototype property of a constructor function. This will be explained in Part 5 of this tutorial (on subtyping).

3.8.2. Factory-based classes

In this approach we define a JavaScript object Person (actually representing a class) with a special create method that invokes the predefined Object.create method for creating objects of type Person:

var Person = {
  typeName: "Person",
  properties: {
    firstName: {range:"NonEmptyString", label:"First name", 
        writable: true, enumerable: true},
    lastName: {range:"NonEmptyString", label:"Last name", 
        writable: true, enumerable: true}
  },
  methods: {
    getInitials: function () {
      return this.firstName.charAt(0) + this.lastName.charAt(0); 
    }
  },
  create: function (slots) {
    var obj=null, properties = this.properties;
    // create object
    obj = Object.create( this.methods, properties);
    // add property slot for direct type
    Object.defineProperty( obj, "type", 
        {value: this, writable: false, enumerable: true});
    // initialize object
    Object.keys( slots).forEach( function (prop) {
      if (properties[prop]) obj[prop] = slots[prop];
    })
    return obj;
  }
};

Notice that our Person object actually represents a factory-based class. An instance of such a factory-based class is created by invoking its create method:

var pers1 = Person.create( {firstName:"Tom", lastName:"Smith"});

The method getInitials is invoked on the object pers1 of type Person by using the 'dot notation', like in the constructor-based approach:

alert("The initials of the person are: " + pers1.getInitials());

Notice that each property declaration for an object created with Object.create has to include the 'descriptors' writable: true and enumerable: true, as in lines 5 and 7 of the Person object definition above.

In a general approach, like in the model-based development framework mODELcLASSjs presented in part 6 of this tutorial, we would not repeatedly define the create method in each class definition, but rather have a generic constructor function for defining factory-based classes. Such a factory class constructor, like mODELcLASS, would also provide an inheritance mechanism by merging the own properties and methods with the properties and methods of the superclass.

3.9. JavaScript as an object-oriented language

JavaScript is object-oriented, but in a different way than classical OO programming languages such as Java and C++. There is no explicit class concept in JavaScript. Rather, classes have to be defined in the form of special objects: either as constructor functions or as factory objects.

However, objects can also be created without instantiating a class, in which case they are untyped, and properties as well as methods can be defined for specific objects independently of any class definition. At run time, properties and methods can be added to, or removed from, any object and class.

3.10. Further reading about JavaScript

Good open access books about JavaScript are

Chapter 2. Building a Minimal Front-End App with BackboneJS in Seven Steps

In this chapter, we show how to build a minimal front-end web application with the BackboneJS framework and the cloud storage service Parse.com. Most parts of the app's code are executed on the front-end, while the persistent storage functionality is executed on the back-end server(s) operated by Parse.

The purpose of our example app is to manage information about books. That is, we deal with a single object type: Book, as depicted in Figure Figure 2.1.

Figure 2.1. The object type Book.

The object type Book.

What do we need for such a data management app? There are four standard use cases, which have to be supported by the app:

  1. Create a new book record by allowing the user to enter the data of a book that is to be added to the collection of stored book records.

  2. Read (or retrieve) all books from the data store and show them in the form of a list.

  3. Update the data of a book record.

  4. Delete a book record.

These four standard use cases, and the corresponding data management operations, are often summarized with the acronym CRUD.

For entering data with the help of the keyboard and the screen of our computer, we can use HTML forms, which provide the user interface technology for web applications.

For maintaining a collection of persistent data objects, we need a storage technology that allows to keep data objects in persistent records on a secondary storage device, either locally or remotely. An attractive option for remote storage is using a cloud storage service, where data is stored in logical data pools, which may physically span multiple servers in a cloud infrastructure owned and managed by a cloud service company. A cloud storage service is typically reachable via a web API, which may be integrated with the data management methods of a framework, as in the case of the cloud storage service provided by Parse.com. For our minimal BackboneJS app, we will therefore use the adapted version of BackboneJS provided by Parse.com.

1. Using the Cloud Storage Service of Parse.com with BackboneJS

Parse.com is a cloud service platform providing a remote storage service that can be invoked from a JavaScript front-end program based on an adapted version of the BackboneJS library. More specifically, for using the storage service we need three BackboneJS/Parse API classes:

  1. Parse.Object allows to define model classes for creating objects that can be saved to, and retrievd from, the Parse data store;

  2. Parse.Query allows to query the Parse data store;

  3. Parse.Collection allows to represent and process collections of objects, such as the population of a table or the result set of a query.

For maintaining a collection of data objects in a persistent data store, the Parse platform provides back-end storage services, that can be invoked via a web API from BackboneJS model objects with methods such as save and destroy using the JavaScript XML HTTP Request (XHR) API for exchanging HTTP messages with the Parse server. In general, sending data to a remote back-end server from a JavaScript front-end program is done with asynchronous remote procedure calls using XHR. Since such a remote procedure call can result either in a reply message confirming the successful completion of the call or indicating an error state, we generally need to specify two JavaScript methods as the arguments for calling the remote procedure asynchronously: the first method is invoked when the remote procedure call has been successfully completed, and the second one when it failed.

1.1. Getting Started with Parse

Any web page of your BackboneJS/Parse app needs to load the BackboneJS/Parse library from the Parse content delivery network with the help of the following HTML script element:

<script src="http://www.parsecdn.com/js/parse-1.3.5.min.js"></script>

For being able to use a Parse service via a BackboneJS/Parse method invocation in your app's JavaScript code, you need to set up a user account on the Parse website. Then, go to the Dashboard and create a new Parse app account using the name of your app. After your Parse app account has been created, you can look up its Application ID and its JavaScript Key under "Application Keys". You need to provide these two keys as the arguments of the Parse.initialize method when you initialize your app, as in the following code pattern:

Parse.initialize("APPLICATION_ID", "JAVASCRIPT_KEY");

1.2. The BackboneJS/Parse Model Layer

1.2.1. Defining a model class

A BackboneJS/Parse model class is defined by extending the predefined class Parse.Object using the function Parse.Object.extend, providing the name of the new class and certain class definition slots. In the simplest case, such a definition only includes a defaults slot for defining the class attributes' default values. This is shown in the following definition of a model class Book, which serves as our running example:

var Book = Parse.Object.extend("Book", {
  defaults: {
    isbn: "",
    title: "",
    year: 0
  }
});

Notice that the defaults slot sort of defines the properties of a class. We create a new object as an instance of a model class in the following way:

var book = new Book({
      isbn: "123456789X", 
      title: "BackboneJS/Parse Tutorial", 
      year: 2013
});

After the object has been created, its property slots are not represented as direct slots (e.g., there is no book.isbn slot), but rather as key-value slots in the predefined map-valued property attributes

We can access the value of a property with the help of the method get as in:

var publicationYear = book.get("year");

We can assign a value to a property with the help of the method set as in:

book.set("year", 2014);

1.2.2. Representing the collection of all instances of a class

For representing the collection of all the instances of a class C managed by the application, we define the class-level property C.instances. This property is set to the Parse collection representing the population of the class in the following way:

Book.instances = (new Parse.Query( Book)).collection();

The class-level property Book.instances is set to the Parse collection that stores the results from the query retrieving the entire population of the Book table with the help of the query object new Parse.Query( Book).

1.2.3. Fetching data from the Parse data store

For initializing a data management use case, we generally need to fetch the current data tfrom the Parse data store. Since Book.instances is a Parse collection, we can apply the fetch method to it for refreshing its contens from the current state of the Parse data store:

Book.instances.fetch({  // load data from Parse data store
  success: function (coll) {
    ...;  // set up the user interface
  },
  error: function (coll, error) {
    console.log("Error: " + error);
  }
});

1.2.4. Writing data to the Parse data store

Writing data to the Parse data store is done with the help of the Parse method save, as shown in the following example

var book = new Book( ...);
book.save( null, {
  success: function (obj) {
    console.log("Saved: " + JSON.stringify(obj));
  },
  error: function (obj, error) {
    console.log("Error: " + error + " for " + JSON.stringify(obj));
  }
});

Notice that, as an asynchronous remote procedure call, the Parse save method is invoked with a success and an error callback method as arguments.

To make sure the data was saved, you can look it up with the web user interface of Parse. You should see something like in the following screen shot.

Figure 2.2. Looking up your app's data with the web user interface of Parse.

Looking up your app's data with the web user interface of Parse.

Three attributes are provided by Parse automatically. The attribute objectId is a unique identifier for each Parse object, while createdAt and updatedAt represent the time that each object was created and last modified in the Parse data store. Each of these attributes is filled in by Parse, so they are read-only in your app's JavaScript code.

1.2.5. Destroying a Parse object

Deleting an existing object from the Parse data store is done with the help of the Parse method destroy:

var book = Book.instances.get( id);
book.destroy({
  success: function (obj) {
    console.log("Deleted book "+ JSON.stringify(obj));
  },
  error: function (obj, error) {
    console.log( error + "\nCannot delete the book " + 
        JSON.stringify(obj) +". Not found in database!");
  }
});

2. Building a Minimal BackboneJS/Parse App in Seven Steps

2.1. Step 1 - Set up the Folder Structure

In the first step, we set up our folder structure for the application. We pick a name for our app, such as "Public Library", and a corresponding (possibly abbreviated) name for the application folder, such as "publicLibrary". Then we create this folder on our computer's disk and add three subfolders: "css" for our CSS style files, "lib" for the libraries used in the app, and "src" for our JavaScript source code files. In the "src" folder, we create the subfolders "model", "view" and "ctrl", following the Model-View-Controller application architecture paradigm. Thus, we get the following folder structure:

publicLibrary
  css
  lib
  src
    ctrl
    model
    view

2.2. Step 2 - Write the Model Code

In the second step, we create the model classes for our app, using one JavaScript file for each model class. In the information model for our example app, shown in Figure Figure 2.1, there is only one class, representing the object type Book. So, in the folder src/model, we create a file Book.js containig the entire code for the model class Book.

The first part of this JavaScript contains the Parse/Backbone model class definition, which for now only includes a defaults property for defining the attributes' default values:

var Book = Parse.Object.extend("Book", {
  defaults: {
    isbn: "",
    title: "",
    year: 0
  }
});

In addition to defining the Parse/Backbone model class, we also define the following items in the Book.js file:

  1. A class-level property Book.instances representing the collection of all Book instances managed by the application.

  2. A class-level method Book.add for creating a new book record in the Parse data store.

  3. A class-level method Book.update for updating a book record in the Parse data store.

  4. A class-level method Book.destroy for deleting a book record from the Parse data store.

2.2.1. Representing the collection of all Book instances

For representing the collection of all Book instances managed by the application, we define and initialize the class-level property Book.instances in the following way:

Book.instances = (new Parse.Query( Book)).collection();

The class-level property Book.instances is set to the Parse collection object obtained by converting the result list from retrieving the population of the Book table with the help of the query object new Parse.Query( Book).

2.2.2. Creating a new Book record

Creating and storing a new book record in the Parse data store is done with the help of the following class-level method:

Book.add = function (slots) {
  var book = null;
  // make sure that year is an integer
  slots.year = parseInt( slots.year);
  book = new Book( slots);
  book.save( null, {
    success: function (obj) {
      console.log("Saved: "+ JSON.stringify(obj));
    },
    error: function (obj, error) {
      console.log("Error: "+ error + " for "+ JSON.stringify(obj));
    }
  });
};

2.2.3. Updating an existing Book record

An existing book record can be updated in the Parse data store witrh the help of the following class-level method:

Book.update = function (id, slots) {
  var book = Book.instances.get( id);
  // make sure that year is an integer
  slots.year = parseInt( slots.year);  
  if (book.title !== slots.title) { 
    book.set("title", slots.title);
  }
  if (book.year !== slots.year) { 
    book.set("year", slots.year);
  }
  book.save( null, {
    success: function (obj) {
      console.log("Saved: "+ JSON.stringify(obj));
    },
    error: function (obj, error) {
      console.log("Error: "+ error + "Model: "+ JSON.stringify(obj));
    }
  });
};

2.2.4. Deleting an existing Book record

Deleting an existing book record in the Parse data store is done witrh the help of the following class-level method:

Book.destroy = function (id) {
  var book = Book.instances.get( id);
  Book.instances.remove( book);
  book.destroy({
    success: function (obj) {
      console.log("Deleted book "+ JSON.stringify(obj));
    },
    error: function (obj, error) {
      console.log( error + "\nCannot delete the book " + 
          JSON.stringify(obj) +". Not found in database!");
    }
  });
};

2.2.5. Creating test data

The following procedure creates 3 book objects and saves them in the Parse data store using Parse.Object.saveAll:

Book.createTestData = function () {
  var book1 = new Book(
        {isbn:"006251587X", title:"Weaving the Web", year:2000});
  var book2 = new Book(
        {isbn:"0465026567", title:"Gödel, Escher, Bach", year:1999});
  var book3 = new Book(
        {isbn:"0465030793", title:"I Am A Strange Loop", year:2008});
  var list = [book1, book2, book3];
  Parse.Object.saveAll( list, {
    success: function (list) {
      console.log("Saved: "+ list.length);
    },
    error: function (error) {
      console.log("Error: "+ error);
    }
  });
};

The Parse user interface allows you to take a look at your Parse tables, and you should see a table like the following.

Table 2.1. Table 2: A collection of book objects represented as a Parse table

ISBN Title Year
006251587X Weaving the Web 2000
0465026567 Gödel, Escher, Bach 1999
0465030793 I Am A Strange Loop 2008

2.3. Step 3 - Initialize the App

We initialize the application in src/ctrl/initialize.js by defining its namespaces and by invoking the Parse.initialize procedure:

var pl = { model:{}, view:{}, ctrl:{} };
Parse.initialize("APPLICATION_ID", "JAVASCRIPT_KEY");

Here, the main namespace is defined to be pl, standing for "public library", with the three subnamespaces model, view and ctrl being initially empty objects.

You have to replace the srtings APPLICATION_ID with your Parse Application ID, and JAVASCRIPT_KEY with your Parse JavaScript Key, which you can look up on your Parse account page.

2.4. Step 4 - Develop the Use Case "List Objects"

This use case represents the "Read" from the four basic data management use cases Create-Read-Update-Delete (CRUD). The simple logic of this use case consists of two steps:

  1. Read the collection of all objects from the persistent data store.

  2. Display each object as a row in a HTML table on the screen.

The user interface for this use case is provided by the HTML file listBooks.html (in the main folder publicLibrary), consisting of the following code:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Minimal Parse/Backbone App Example</title>
  <script src="http://www.parsecdn.com/js/parse-1.3.5.min.js"></script>
  <script src="src/ctrl/initialize.js"></script>
  <script src="src/model/Book.js"></script>
  <script src="src/view/listBooks.js"></script>
  <script src="src/ctrl/listBooks.js"></script>
  <script>
    window.addEventListener("load", pl.ctrl.listBooks.initialize);
  </script>
</head>
<body>
  <h1>Public Library: List all books</h1>
  <table id="books">
    <thead><tr><th>ISBN</th><th>Title</th><th>Year</th></tr></thead>
    <tbody></tbody>
  </table>
</body>
</html>

Notice that this HTML file, which is used for running the "list books" use case, loads four JavaScript files: the Parse function library parse-1.3.5.min.js from the Parse website, the application initialization code file src/ctrl/initialize.js, the model code file src/model/Book.js, the view code file src/view/listBooks.js and the controller code file src/ctrl/listBooks.js. While the model code file and the application initialization code file have been discussed above, we now develop the "list books" use case's controller and view code files.

For initializing this use case, we have the following code in src/ctrl/listBooks.js:

pl.ctrl.listBooks = {
  initialize: function () {
    Book.instances.fetch({  // load data from Parse data store
      success: function (coll) {
        pl.view.listBooks.setUpUserInterface(); 
      },
      error: function (coll, error) {
        console.log("Error: " + error);
      }
    });
  }
};

Notice that the initialize procedure invokes the setUpUserInterface procedure when the instances of the Book model class can be fetched from the Parse data store (in the success callback).

The setUpUserInterface procedure sets up the user interface by constructing a HTML table populated with the book records from the Book.instances collection:

pl.view.listBooks = {
  setUpUserInterface: function () {
    var tableBodyEl = document.querySelector("table#books>tbody");
    Book.instances.forEach( function (bkObj) {
      var row = tableBodyEl.insertRow(-1);
      row.insertCell(-1).textContent = bkObj.get("isbn");      
      row.insertCell(-1).textContent = bkObj.get("title");  
      row.insertCell(-1).textContent = bkObj.get("year");
    });
  }
};

The view table is created in a loop over all elements of the Parse collection Book.instances. In each step of this loop, a new HTML table row is created in the table body element with the help of the DOM operation insertRow, and then three cells are created within this row with the help of the DOM operation insertCell: the first one for the isbn property value of the book object, and the second and third ones for its title and year property values, which are accessed using the get method on the book Parse object. Both insertRow and insertCell have to be invoked with the argument -1 for making sure that new elements are appended to the list of rows and cells.

2.5. Step 5 - Develop the Use Case "Create Object"

The HTML user interface for the use case "create book" requires a form with a form field for each attribute of the Book model class. For our example app, this page would be called createBook.html (in the main folder publicLibrary) and would contain the following HTML code:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Minimal Parse/Backbone App Example</title>
    <script src="http://www.parsecdn.com/js/parse-1.3.5.min.js"></script>
    <script src="src/ctrl/initialize.js"></script>
    <script src="src/model/Book.js"></script>
    <script src="src/view/createBook.js"></script>
    <script src="src/ctrl/createBook.js"></script>
    <script>
      window.addEventListener("load", pl.ctrl.createBook.initialize);
    </script>
  </head>
  <body>
    <h1>Public Library: Create a new book record</h1>
    <form id="Book">
      <p><label>ISBN: <input name="isbn" /></label></p>
      <p><label>Title: <input name="title" /></label></p>
      <p><label>Year: <input name="year" /></label></p>
      <p><button type="button" name="commit">Save</button></p>
    </form>
    <p><a href="index.html">Main menu</a></p>
  </body>
</html>

As in all four use cases, we load both a use-case-specific controller code file, src/ctrl/createBook.js, and a view code file:, src/view/createBook.js, which are presented in the next two program listings.

The controller code file just contains an initialize procedure for initializing the use case in the same way as for the "list books" use case, except that after succesfully fetching all book records from the Parse data store, the procedure for settiing up the "create book" user interface is invoked:

pl.ctrl.createBook = {
  initialize: function () {    
    Book.instances.fetch({  // load data from Parse data store
      success: function (coll) {
        pl.view.createBook.setUpUserInterface(); 
      },
      error: function (coll, error) {
        console.log("Error: " + error);
      }
    });
  }
};

The view code file just contains the setUpUserInterface procedure for setting up the HTML user interface of the use case:

pl.view.createBook = {
  setUpUserInterface: function () {
    var saveButton = document.forms['Book'].commit;
    saveButton.addEventListener("click", function (e) {
      var formEl = document.forms['Book'];
      var slots = { isbn: formEl.isbn.value, 
            title: formEl.title.value, 
            year: formEl.year.value
          };
      Book.add( slots);
      formEl.reset();
    });
  }
};

In the event handler for clicking the save button, the form field values corresponding to the Book attributes isbn, title and year are retrieved and passed to the method Book.add for creating and storing a new book record. Finally, the "create book" form is reset

2.6. Step 6 - Develop the Use Case "Update Object"

In the form for the "update book" user interface (publicLibrary/updateBook.html), the form field for the standard identifier attribute isbn is an HTML output element because the user is not allowed to change the standard identifier of an object in the "update object" use case. Otherwise, the form is very similar to createBook.html. It contains the following HTML code:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Minimal Parse/Backbone App Example</title>
    <script src="http://www.parsecdn.com/js/parse-1.3.5.min.js"></script>
    <script src="src/ctrl/initialize.js"></script>
    <script src="src/model/Book.js"></script>
    <script src="src/view/updateBook.js"></script>
    <script src="src/ctrl/updateBook.js"></script>
    <script>
      window.addEventListener("load", pl.ctrl.updateBook.initialize);
    </script>
  </head>
  <body>
    <h1>Public Library: Update a book record</h1>
    <form id="Book">
      <p><label>Select book: 
        <select name="selectBook"><option value="0">-----</option></select>
      </label></p>
      <p><label>ISBN: <output name="isbn" /></label></p>
      <p><label>Title: <input name="title" /></label></p>
      <p><label>Year: <input name="year" /></label></p>
      <p><button type="button" name="commit">Save Changes</button></p>
    </form>
    <p><a href="index.html">Main menu</a></p>
  </body>
</html>

The controller code file just contains an initialize procedure for initializing the use case in the same way as before:

pl.ctrl.updateBook = {
  initialize: function () {
    // load book data from Parse DB table
    Book.instances.fetch({
      success: function (coll) {
        pl.view.updateBook.setUpUserInterface(); 
      },
      error: function (coll, error) {
        console.log("Error: " + error);
      }
    });
  }
};

The view code file just contains the setUpUserInterface procedure for setting up the HTML user interface of the use case:

pl.view.updateBook = {
  setUpUserInterface: function () {
    var formEl = document.forms['Book'],
        saveButton = formEl.commit,
        selectBookEl = formEl.selectBook;
    // populate the selection list
    Book.instances.forEach( function (book) {
      var bookOptionEl = document.createElement("option");
      bookOptionEl.text = book.get("title");
      bookOptionEl.value = book.id;  // the Parse object ID
      selectBookEl.add( bookOptionEl, null);
    });
    // when a book is selected, fill the form with its data
    selectBookEl.addEventListener("change", function () {
      var book = null, 
          bookObjId = selectBookEl.value;
      if (bookObjId !== "0") {
        book = Book.instances.get( bookObjId);
        formEl.isbn.value = book.get("isbn");
        formEl.title.value = book.get("title");
        formEl.year.value = book.get("year");
      } else {  // no book selected
        formEl.isbn.value = "";
        formEl.title.value = "";
        formEl.year.value = "";
      }
    });
    //  event handler for clicking the save button
    saveButton.addEventListener("click", function () {
      var formEl = document.forms['Book'];
      var bookObjId = formEl.selectBook.value;
      var slots = { isbn: formEl.isbn.value, 
            title: formEl.title.value, 
            year: formEl.year.value
          };
      Book.update( bookObjId, slots);
      formEl.reset();
    });
  }
};

This procedure has three parts:

  1. The book selection list is populated with the titles of the books that are already stored, while the value of each selection option is set to the book's Parse object ID.

  2. In a change event handler for the book selection field, we populate the form with the data of the selected book whenever a book is selected.

  3. In the event handler for clicking the update button we invoke the method Book.update for updating the selected book record, and then reset the "update book" form.

2.7. Step 7 - Develop the Use Case "Delete Object"

In the form for the "delete book" user interface (publicLibrary/deleteBook.html), we just need a book selection field, as in updateBook.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Minimal Parse/Backbone App Example</title>
    <script src="http://www.parsecdn.com/js/parse-1.2.8.min.js"></script>
    <script src="src/ctrl/initialize.js"></script>
    <script src="src/model/Book.js"></script>
    <script src="src/view/deleteBook.js"></script>
    <script src="src/ctrl/deleteBook.js"></script>
    <script>
      window.addEventListener("load", pl.ctrl.deleteBook.initialize);
    </script>
  </head>
  <body>
    <h1>Public Library: Delete a book record</h1>
    <form id="Book">
      <p>
        <label>Select book: <select name="selectBook"></select></label>
      </p>
      <p><button type="button" name="commit">Delete</button></p>
    </form>
    <p><a href="index.html">Main menu</a></p>
  </body>
</html>

The controller code file again just contains an initialize procedure for initializing the use case by fetching all book records from the Parse data store and invoking the setUpUserInterface procedure:

pl.ctrl.deleteBook = {
  initialize: function () {
    // load book data from Parse DB table
    Book.instances.fetch({
      success: function (coll) {
        pl.view.deleteBook.setUpUserInterface(); 
      },
      error: function (coll, error) {
        console.log("Error: " + error);
      }
    });
  }
};

The view code file just contains the setUpUserInterface procedure for setting up the HTML user interface of the use case:

pl.view.deleteBook = {
  setUpUserInterface: function () {
    var deleteButton = document.forms['Book'].commit;
    var selectEl = document.forms['Book'].selectBook;
    // populate the select list with books
    Book.instances.forEach( function (book) {
      var bookOptionEl = document.createElement("option");
      bookOptionEl.text = book.get("title");
      bookOptionEl.value = book.id;  // the Parse object ID
      selectEl.add( bookOptionEl, null);
    });
    deleteButton.addEventListener("click", function () {
      var selectEl = document.forms['Book'].selectBook,
          id = selectEl.value;
      if (id) {
        Book.destroy( id);
        selectEl.remove( selectEl.selectedIndex);
      }
    });
  }
};

This procedure has two parts:

  1. The book selection list is populated with the titles of the books that are already stored, while the value of each selection option is set to the book's Parse object ID.

  2. In the event handler for clicking the delete button we invoke the method Book.destroy for deleting the selected book record, and then also remove the book from the options of the selection list.

3. Run the App and Get the Code

You can run the minimal app from our server or download the code as a ZIP archive file. Recall that you have to edit the src/ctrl/initialize.js file and enter your Parse APPLICATION_ID and JAVASCRIPT_KEY before you run it, as discussed above.

4. Points of Attention

The code of this app should be extended by

  • adding some CSS styling for the user interface pages and

  • adding constraint validation.

We show how to do this in the follow-up tutorial Adding Constraint Validation.

Notice that in this tutorial, we have made the assumption that all application data can be loaded into main memory (like all book data is loaded into the collection Book.instances). In the case of remote storage, this approach only works for very small databases. Otherwise, it's no longer possible to load the entire population of all tables into main memory, but we have to use a technique where only parts of the table contents are loaded.

Another issue with the do-it-yourself code of this example app is the boilerplate code needed per class for the data storage management methods add, update, and destroy. While it is good to write this code a few times for learning app development, you don't want to write it again and again later when you work on real projects. In another tutorial, we present an approach how to put these methods in a generic form in a meta-class called mODELcLASS, such that they can be reused in all model classes of an app.