Chapter 13. Implementing Unidirectional Non-Functional Associations with Plain JavaScript

Table of Contents

1. Implementing Multi-Valued Reference Properties in JavaScript
2. Make a JavaScript Data Model
3. New issues
4. Write the Model Code
4.1. Encode the add and remove operations
4.2. Implement a deletion policy
4.3. Serialization and De-Serialization
5. Write the User Interface Code
5.1. Show information about associated objects in the List Objects use case
5.2. Allow selecting associated objects in the create use case
5.3. Allow selecting associated objects in the update use case
6. How to Run the App, Get the Code and Ask Questions
7. Points of Attention

A unidirectional non-functional association is either one-to-many or many-to-many. In both cases such an association is represented, or implemented, with the help of a multi-valued reference property.

In this chapter, we show

  1. how to derive a JavaScript data model from an association-free information design model with multi-valued reference properties representing unidirectional non-functional associations,

  2. how to encode the JavaScript data model in the form of JavaScript model classes,

  3. how to write the view and controller code based on the model code.

1. Implementing Multi-Valued Reference Properties in JavaScript

A mulit-valued reference property, such as the property authors of the object type Book, allows storing a set of references to objects of some type, such as Author. When creating a new object of type Book, the constructor function needs to have a parameter for providing a suitable value for this reference property. In JavaScript we can allow this value to be a set (or list) of internal object references or of ID references, as shown in the following example:

function Book( slots) {
  // set the default values for the parameter-free default constructor
  ...
  this.authors = {};   // map of Author object referencess
  ...
  // constructor invocation with a slots argument
  if (arguments.length > 0) {
    ...
    this.setAuthors( slots.authors || slots.authorsIdRef);
    ...
  }
}

Notice that the constructor parameter slots is expected to contain either an authors or an authorsIdRef slot. The JavaScript expression slots.authors || slots.authorsIdRef, using the disjunction operator ||, evaluates to authors, if slots contains an object reference slot authors, or to authorsIdRef, otherwise. We handle the resulting ambiguity in the property setter by checking the type of the argument as shown in the following code fragment:

Book.prototype.setAuthors = function (a) {
  var keys=[], i=0;
  this.authors = {};
  if (Array.isArray(a)) {  // array of ID references
    for (i= 0; i < a.length; i++) {
      this.addAuthor( a[i]);
    }
  } else {  // map of object references
    keys = Object.keys( a);
    for (i=0; i < keys.length; i++) {
      this.addAuthor( a[keys[i]]);
    }
  }
};

A multi-valued reference property can be implemented as a property with either an array, or a map, of object references, as its value. We prefer using maps for implementing multi-valued reference properties since they guarantee that each element is unique, while with an array we would have to prevent duplicate elements. Also, an element of a map can be easily deleted (with the help of the delete operator), while this requires more effort in the case of an array. But for implementing list-valued reference properties, we need to use arrays.

We use the standard identifiers of the referenced objects as keys. If the standard identifier is an integer, we must take special care in converting ID values to strings for using them as keys.