Leap
Hello World Tutorial
Download

"Hello Leap"

Leap is a Model Oriented Design (MOD) framework for JavaScript.

This tutorial demonstrates using Leap to create a simple Hello World MOD single page app.

Model Oriented Design (MOD) Development

Model Oriented Design (MOD - pronounced mode) uses task-specific data models to store & manipulate information related to performing a task. Operators on the data (such as UI's or back-end services) manipulate the shared data models, thus maintaining a coordinated, single view of the state of the task. For example, if we have a UI to enter or edit a person's name, the model could contain a person object with a name property.

When UI components receive user input, the model is changed (not just the UI widget) thus reflecting the change throughout the entire application. This means that changes are automatically reflected on all application components related to the data, including both UI components and back-end services, such as REST calls.

Using models to hold task-related data has several compelling advantages:

Leap MOD

Leap implements MOD using standard HTML & JavaScript, providing an automated model data-store. Using standard HTML & JavaScript means:

Getting Started with Leap

To get started, create a new file hello.html and include the leap.min.js library in your HTML file and start leap from a script tag:

<!DOCTYPE html>
<html>
   <head>
      <script src="http://leapjs.org/latest/leap.min.js"></script>
      <script>
         leap.start();
      </script>
   </head>
</html>

leap.start() runs only when the page is fully loaded. It accepts an optional function as a parameter which can be used to execute page start-up JavaScript code that is to be run only after the page is fully loaded.

Creating a Model

Creating a model in Leap is simple - just annotate any widget with a data model path. Leap does the rest, automatically creating the model and associating the widget with it. Additionally, Leap automatically watches the widget for changes and updates the model so you don't have to do any additional work to keep things in sync.

<input type="text" data-field="person.name" value="{{person.name}}"/>

The data-field attribute links the input to the person model's name property. Therefore, a user changing the value of the input automatically changes the model.

Data models are automatically created whenever a data-field attribute is encountered. Data models can also be explicitly declared using JavaScript Leap calls, such as leap.set(). See the Leap Tutorial for details.

Open the hello.html file in a browser. You will see the input field on the page. You can observe the linking of the model to the UI by typing a value in the input box, hitting return, then opening up the web browser's console & displaying the value of the person.name model property by entering:

leap.models.person.name
"Joe"

You will see the value you have typed in the input box.

Data model paths take the form objectName.property for Objects and arrayName[index] for Arrays. Data model paths can be any length or depth. For example person.address.city represents a property of a sub-object. people[0].name represents a property of the first object element of an array.
While JavaScript allows for bracketed property notation for object properties, such as person[name], Leap strictly interprets bracketed notations as arrays. For object properties, be sure to use the objectName.property notation.

Referencing Model Data

The data model can be referenced throughout an HTML file, in text or in element attributes, using a data model expression that encloses a data model path inside {{ }}. A data model expression is replaced by the current data model value and is kept in sync as the model changes.

The Hello Leap input element's value attribute references the data model using the {{person.name}} expression. Whenever the model changes, the expression is automatically refreshed, displaying the latest model value.

Data model expressions can appear anywhere in the HTML. For example, a div can be added to display the current value of the data model:

<div>Hello {{person.name}}</div>

Whenever the data model is changed, the div will be changed to reflect the new value.

Changing Model Data

Whenever the input value changes, the person model's name property changes accordingly. Likewise, if you set the model's value in the web browser's console, the model will change along with all related references, such as the input and div elements:

leap.models.person.name='Sally'
"Sally"

Validation

In MOD, data validation is performed on the model not on the widgets. Validation at the model level ensures that ALL actors on the data are validated and ensured to be correct. This includes data modifications from the UI as well as any back-end services providing data in the form of a response. As such, data validators are added to a data model directly, such as:

leap.addValidator('person.name',val=>(val && val.length > 1 ? "" : "name must have at least two characters"));
Leap provides several built-in validators, such as for checking length, not-null, value and regex matches. See the Leap Tutorial for detailed examples.

Errors produced by validators are held alongside the data in the model and are passed to any error handlers that are listening for errors in the model data.

Error Handling

When a data model encounters errors, the errors are sent to error handlers that have been attached to the data model. For example, to catch errors in the person data model, add an error listener:

leap.addErrorListener('person', (model,errors)=>alert(errors.reduce((p,c)=>p+c+" ","")));

A listener can be added to any node in the model. Here the listener has been added to the person node, meaning that the handler will be executed for any errors that occur in the person node or any of its offspring. As such, errors in the person.name property will cause the error handler to be run. To process only errors for the person.name property, the listener would instead be added directly to the person.name path:

leap.addErrorListener('person.name', (model,errors)=>alert(errors.reduce((p,c)=>p+c+" ","")));

Data Listeners

Data listeners, like error listeners, are added to a data model to capture changes in the model. For example to catch changes in the person data model:

leap.addDataListener('person', 'change', (obj,prop,before,after)=>alert(prop+" changed from "+before+" to "+after));

Several events are fired for data model changes, including read, add, change, remove, error, success & final (as well as related before & after events for add, change & remove events).

Code

The Full Code

<!DOCTYPE html>
<html>
   <head>
      <script src="http://leapjs.org/latest/leap.min.js"></script>
      <script>
         leap.start(()=>{
            leap.addValidator('person.name', val=>(val && val.length > 1 ? "" : "name must have at least two characters"));
            leap.addErrorListener('name', (model,errors)=>alert(errors.reduce((p,c)=>p+c+" ","")));
            leap.addDataListener('name', 'change', (model,obj,prop,before,after)=>alert(prop+" changed from "+before+" to "+after));
         });
       </script>
   </head>
   <body>
      <input type="text" data-field="person.name" value="{{person.name}}"/>
      <button>SAY HI</button>
      <div>Hello {{person.name}}</div>
   </body>
</html>

Simplified JavaScript Code

For those less familiar with JavaScript, below is a less condensed version of the code:

<!DOCTYPE html>
<html>
<head>
<script src="http://leapjs.org/latest/leap.min.js"></script>
<script>
function nameValidator(value) {
   if( !value || value.length <= 1 ) {
      return "name must have at least two characters";
   }
   return "";
}
function errorHandler( model, errors ) {
   let message = "";
   if( errors ) {
      for( const error of errors ) {
         message += ( message != "" ? ', ' : '' ) + error;
      }
   }
   alert( message );
}
function changeHandler( model, obj, prop, beforeValue, afterValue ) {
   alert( prop + " changed from " + beforeValue + " to " + afterValue );
}
leap.start( function(){
   leap.addValidator('person.name', nameValidator );
   leap.addErrorListener('person', errorHandler );
   leap.addDataListener('person', 'change', changeHandler );
});
</script>
</head>
<body>
   <input type="text" data-field="person.name" value="{{person.name}}" />
   <button>SAY HI</button>
   <div>Hello {{person.name}}</div>
</body>
</html>