Click here to Skip to main content
15,881,715 members
Articles / Web Development / HTML

Declarative and Responsive Constraint Validation with mODELcLASSjs

Rate me:
Please Sign up or sign in to vote.
4.94/5 (7 votes)
24 Aug 2016CPOL9 min read 21.5K   105   8   3
Avoiding boilerplate code for constraint validation in a JavaScript front-end web app by using the model-based framework mODELcLASSjs

Introduction

This tutorial is a short version of a chapter from the open access book Building Front-End Web Apps with Plain JavaScript. We show how to build a single-class front-end web app with constraint validation using the model-based development framework mODELcLASSjs, which helps to avoid repetitive code structures ("boilerplate code"). A front-end web application can be provided by any web server, but it is executed on the user's computer device (smartphone, tablet or notebook), and not on the remote web server. Typically, but not necessarily, a front-end web application is a single-user application, which is not shared with other users.

We show how to express integrity constraints declaratively in a model class defined as an instance of the meta-class mODELcLASS, and how to validate data in the model code layer of the app as well as perform responsive constraint validation in the user interface layer. If you want to see how it works, you can run the mODELcLASS validation app discussed in this article from our server.

The simple form of a JavaScript data management application presented in this tutorial takes care of only one object type ("books") for which it supports the four standard data management operations (Create/Retrieve/Update/Delete). It improves the validation app discussed in the plain JavaScript validation tutorial by avoiding boilerplate code in the model layer

  1. for checks and setters, and

  2. for data storage management

with the help of the model-based development framework mODELcLASSjs.

Background

In the Model-View-Controller (MVC) paradigm, the user interface (UI) is called 'view', and the term 'controller' denotes the glue code needed for integrating the UI code with the model classes, or, in MVC jargon, for integrating the 'view' with the 'model'. Using a model-based development approach, the model classes of an app are obtained by encoding the app's data model, which is typically expressed in the form of a UML class diagram. Since it is the task of the model layer to define and validate constraints and to manage data storage, we need reusable model code taking care of this in a generic manner for avoiding per-class and per-property boilerplate code for constraint validation, and per-class boilerplate code for data storage management. This is where mODELcLASSjs comes in handy. It provides a generic check method for validating property constraints, and the generic storage management methods add, update and destroy for creating new (persistent) objects/rows, for updating existing objects/rows and for deleting them.

We must not confuse the term 'model' as used in the MVC paradigm, and adopted by many web development frameworks, and the term 'model' as used in UML and other modeling languages. While the former refers to the model classes of an app, the latter refers to the concept of a model either as a simplified description of some part of the real world, or as as design blueprint for construction.

In model-based engineering, models are the basis for designing and implementing a system, no matter if the system to be built is a software system or another kind of complex system such as a manufacturing machine, a car, or an organisation.

In model-based software development, we distinguish between three kinds of models:

  1. solution-independent domain models describing a specific part of the real-world and resulting from requirements and domain engineering in the system analysis, or inception, phase of a development project;
  2. platform-independent design models specifying a logical system design resulting from the design activities in the elaboration phase;
  3. platform-specific implementation models (including data models) as the result of technical system design in the implementation phase.

Domain models are the basis for designing a software system by making a platform-independent design model, which is in turn the basis for implementing a system by making an implementation model and encoding it in the language of the chosen platform. Concerning information modeling, we first make a domain information model, then we derive an information design model from it, and finally map the information design model to a data model for the chosen platform. With an object-oriented programming (OOP) approach, we encode this data model in the form of model classes, which are the basis for designing and implementing a data management user interface (UI).

mODELcLASSjs facilitates model-based app development by allowing a direct encoding of an information design model.

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 objetcs that instantiate it.

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.

Since we normally need to define class hierarchies, and not just single classes, these two alternative approaches cannot be mixed within a class hierarchy, and we have to make a choice whenever we build an app. With mODELcLASSjs, you choose the second approach with the following benefits:

  1. Properties are declared (with a property label, a range and many other constraints)

  2. Support of object pools

  3. Support of multiple inheritance and multiple classification

Using mODELcLASSjs

We show how to build a single-class data management app with constraint validation using the model-based development framework mODELcLASSjs for avoiding boilerplate model code. Compared to the plain JavaScript validation tutorial, we deal with the same issues: showing 1) how to define constraints in a model class, 2) how to perform responsive validation in the user interface based on the constraints defined in the model classes. The main difference when using mODELcLASSjs is that defining constraints becomes much simpler. The check methods used in the plain JavaScript validation tutorial are no longer needed. Since constraints are defined in a purely declarative manner, their textual encoding corresponds directly to their expression in the information design model. This implies that we can directly encode the information design model without first creating a data model from it.

As in our plain JavaScript validation tutorial, the purpose of our app is to manage information about books. The information items and constraints are described in the following information design model.

Design Model

Encoding the Design Model

We now show how to encode the ten integrity constraints defined by the design model shown above.

  1. For the first three of the four properties defined in the Book class, we have a mandatory value constraint, indicated by the multiplicity expression [1]. However, since properties are mandatory by default in mODELcLASSjs, we don't have to encode anything for them. Only for the property edition, we need to encode that it is optional with the key-value pair optional: true, as shown in the edition property declaration in the class definition below.

  2. The isbn attribute is declared to be the standard identifier of Book. We encode this (and the implied uniqueness constraint) in the isbn property declaration with the key-value pair stdid: true, as shown in the class definition below.

  3. The isbn attribute has a pattern constraint requiring its values to match the ISBN-10 format that admits only 10-digit strings or 9-digit strings followed by "X". We encode this with the key-value pair pattern:/\b\d{9}(\d|X)\b/ and the special constraint violation message defined by patternMessage:"The ISBN must be a 10-digit string or a 9-digit string followed by 'X'!".

  4. The title attribute has a string length constraint with a maximum of 50 characters. This is encoded with max: 50.

  5. The year attribute has an interval constraint with a minimum of 1459 and a maximum that is not fixed, but provided by the utility function nextYear(). We can encode this constraint with the key-value pairs min: 1459 and max: util.nextYear().

  6. Finally, there are four range constraints, one for each property. We encode them with corresponding key-value pairs, like range:"NonEmptyString".

This leads to the following definition of the model class Book :

JavaScript
Book = new mODELcLASS({
  typeName: "Book",
  properties: {
    isbn: {range:"NonEmptyString", stdid: true, label:"ISBN", pattern:/\b\d{9}(\d|X)\b/, 
        patternMessage:"The ISBN must be a 10-digit string or a 9-digit string followed by 'X'!"},
    title: {range:"NonEmptyString", max: 50, label:"Title"},
    year: {range:"Integer", min: 1459, max: util.nextYear(), label:"Year"},
    edition: {range:"PositiveInteger", optional: true, label:"Edition"}
  }
});

For such a model class definition, mODELcLASSjs provides generic data management operations (Book.add, Book.update, Book.destroy, etc.) as well as property checks and setters (Book.check and object.set).

After defining a model class, you can create new 'model objects' instantiating it by invoking the create method provided by mODELcLASS:

JavaScript
var book1 = Book.create({isbn:"006251587X", title:"Weaving the Web", year: 2000});

You can then apply the following properties and methods, all pre-defined by mODELcLASS

  1. the property type for retrieving the object's direct type,

  2. the method toString() for serializing an object,

  3. the method set( prop, val) for setting an object property after checking all property constraints,

  4. the method isInstanceOf( Class) for testing if an object is an instance of a model class.

The use of these mODELcLASS features is illustrated by the following examples:

JavaScript
console.log( book1.type.typeName);  // "Book"
console.log( book1.toString());  // "Book{ isbn:"006251587X", ...}"
book1.set("year", 1001);  // "IntervalConstraintViolation: Year must not be smaller than 1459"
book1.set("year", 2001);  // change the year to 2001
console.log( book1.isInstanceOf( Book));  // true

Project Set-Up

The MVC folder structure of this project is the same as in our plain JavaScript validation tutorial. Also, the same library files are used.

The start page of the app first takes care of the page styling by loading the Pure CSS base file (from the Yahoo site) and our main.css file with the help of the two link elements (in lines 6 and 7), then it loads several JavaScript library files (in lines 8-12), including the mODELcLASS file, the app initialization script initialize.js from the src/ctrl folder and the model class Book.js from the src/model folder.

HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
 <meta charset="UTF-8" />
 <title>JS Frontend Validation App Example</title>
 <link rel="stylesheet" type="text/css" 
     href="http://yui.yahooapis.com/combo?pure/0.3.0/base-min.css" />
 <link rel="stylesheet" type="text/css" href="css/main.css" /> 
 <script src="lib/browserShims.js"></script>
 <script src="lib/util.js"></script>
 <script src="lib/errorTypes.js"></script>
 <script src="lib/mODELcLASS.js"></script>
 <script src="src/ctrl/initialize.js"></script>
 <script src="src/model/Book.js"></script>
</head>
<body>
 <h1>Example: Public Library</h1>
 <h2>mODELcLASS Validation App</h2>
 <p>This app supports the following operations:</p>
 <menu>
  <li><a href="listBooks.html"><button type="button">List all books</button></a></li>
  <li><a href="createBook.html"><button type="button">Add a new book</button></a></li>
  <li><a href="updateBook.html"><button type="button">Update a book</button></a></li>
  <li><a href="deleteBook.html"><button type="button">Delete a book</button></a></li>
  <li><button type="button" onclick="Book.clearData()">Clear database</button></li>
  <li><button type="button" onclick="Book.createTestData()">Create test data</button></li>
 </menu>
</body>
</html>

The User Interface

The user interface (UI) is the same as in our plain JavaScript validation tutorial. There is only one difference. For responsive constraint validation, where input event handlers are used to check constraints on user input, the generic check functon Book.check is used:

pl.view.createBook = {
  setupUserInterface: function () {
    var formEl = document.forms['Book'],
        submitButton = formEl.commit;
    submitButton.addEventListener("click", 
        this.handleSubmitButtonClickEvent);
    formEl.isbn.addEventListener("input", function () { 
        formEl.isbn.setCustomValidity( 
            Book.check("isbn", formEl.isbn.value).message);
    });
    formEl.title.addEventListener("input", function () { 
        formEl.title.setCustomValidity( 
            Book.check("title", formEl.title.value).message);
    });
   ...
  },
};

While the validation on user input enhances the usability of the UI by providing immediate feedback to the user, validation on form submission is even more important for catching invalid data. Therefore, the event handler handleSubmitButtonClickEvent() performs the property checks again, as shown in the following program listing:

handleSubmitButtonClickEvent: function () {
  var formEl = document.forms['Book'];
  var slots = { isbn: formEl.isbn.value, 
        title: formEl.title.value,
        year: formEl.year.value,
        edition: formEl.edition.value
  };
  // set error messages in case of constraint violations
  formEl.isbn.setCustomValidity( Book.check( "isbn", slots.isbn).message);
  formEl.title.setCustomValidity( Book.check( "title", slots.title).message);
  formEl.year.setCustomValidity( Book.check( "year", slots.year).message);
  formEl.edition.setCustomValidity( Book.check( "edition", slots.edition).message);
  // save the input data only if all of the form fields are valid
  if (formEl.checkValidity()) {
    Book.add( slots);
  }
}

Concluding Remarks

After eliminating the model layer boilerplate code for constraint validation and for the data storage management methods, there is still a lot of boilerplate code needed in the UI. In a follow-up article, we will present an approach how to avoid this UI boilerplate code.

History

  • 9 September 2015, corrected a reference and the formating
  • 27 October 2014, first version created.
  • 29 October 2014, added usage examples,

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer
Germany Germany
Researcher, developer, instructor and cat lover.

Co-Founder of web-engineering.info and the educational simulation website sim4edu.com.

Comments and Discussions

 
QuestionThe model in MVC Pin
GeorgeyPorgy3-Nov-14 18:07
professionalGeorgeyPorgy3-Nov-14 18:07 
AnswerRe: The model in MVC Pin
Gerd Wagner4-Nov-14 1:01
professionalGerd Wagner4-Nov-14 1:01 
GeneralMy vote of 5 Pin
majid torfi27-Oct-14 19:27
professionalmajid torfi27-Oct-14 19:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.