Modeljs Example

This page is an example of how one may use modeljs. It demos the many features of modeljs by using modeljs to create a table widget (show below) and using that widget on this page. You can look directly at the source of how this is done. But I would recommend first going through the tutorial below.

Tutorial

Often when developing an software application of a moderate size an architecture using some varient of MVC is used. Modeljs was created to be the javascript library for the model portion of the MVC pattern for whichever MVC javascript architecture used. This page is an example of how to use Modlejs to create an application model and use that model to create a table widget (AKA the view). The buttons and samplecode below would be the logic found in the controllers. After completing this tutorial hopefully you can see the value of modeljs for whatever purpose brought you here.

Table:

Step 1: The inputs below can be inserted in the table by clicking the "add row" button. While reset will reload the initial state of the table. Play around with the buttons. Notice if you close the page and come back to it your state is saved. This is done using HTML local storage

Month: Savings:

Step 2: press F12 to bring up firebug or your browsers equivlant debugger. Inspect the implementation of the buttons above. If you are curious about the widget implementation you can look at it now or wait till after the tutorial when you have a better understanding of the api.

Step 3: In the debugger console type the javaScript code below to learn how to use the modeljs api:


model // this is the modeljs model created and used by this page. Inspect it.
model.getValue(); // this returns the value of the model you were just inspecting. This should be easier to inspect, since it's just a JSON object with the model Values only (no metadata). Notice the table property. We bind this object to the table widget. More on this later.

 // This is how you would traversing the Model and perform operations
model.table.data;
model.exampleProperty.getValue();
model.table.data[1][1].getValue();
model.table.data[1][1].setValue(999999); // Notice this changed the value on the table.

/* Lets go through some features of modeljs using model and tableWidget found on this page */

/** Feature: Metadata **/
// Every model object has a metadata object associated with it.
model.exampleProperty.getMetadata();

/** Feature: custom metadata **/
// You can easily add any custom metadata values directly to this object.
model.exampleProperty.getMetadata().exampleCustomMetadata = "custom value";

/** Feature: load/save **/
// Model Metadata can be saved and loaded with the model. We've already see how closing and reopening the page keeps your state. This is done by saving to JSON and loading from JSON.
model.toJSON(); // returns the model JSON to persist.
model.toJSON(true); // passing true will include the model metadata as well.

// To restore your model from the JSON just pass it to the constructor to re-create the model.
var tableModelFromJSON = new Model(model.table.toJSON(true));

// This is esentially what clone does for you.
/** Feature: clone() **/
var clonedTableModel = model.table.clone();

/** Feature: do not persist **/
// if you don't want a property to be presisted, just indicate so in the metadata.
model.exampleProperty.getMetadata().doNotPersist = true;

model.toJSON(); // Notice it has been nullified..

// This page uses your browser local storage to save/load the model. read http://diveintohtml5.info/storage.html#halma for more about this.

/** Feature: getName() **/
// get Name returns the fully qualified name of the property.
model.table.data.getName(); // === "/root/table/data"
// but we can also get the shortName
model.table.data.getShortName(); // === "data"

//Notice the difference between the name of the tableModelFromJSON and your clone. If you metadata object passed to the model constructor does not have a 'name' key/value pair it will always be named /root. read the jsdoc to learn more about the Model constructor and what clone does exactly.
tableModelFromJSON.getName(); // === "/root"
clonedTableModel.getName(); // === "/table"

// Let look at other thing stored in the metadata
/** Feature: validators **/
// We can add validation functions to any Property. Lets create a validator that restricts the value to being a string.
model.exampleProperty.getMetadata().validator = function (value) {
    return typeof value === 'string'
    };
// Now only strings can be assigned to this property. Lets test this.
model.exampleProperty.setValue(100);
model.exampleProperty.getValue(); // setValue didn't do anything. it fails silently.

// We can explicitly test validation before assignment by calling validateValue();
model.exampleProperty.validateValue(100); // returns false;
model.exampleProperty.hasValidator(); // tell you if a validator exists

// In addition to validating input values we can also define how our output values should be formatted by calling getFormattedValue()
/** Feature: getFormattedValue() **/
// By default there is no Formatter defined and getFormattedValue() will return the raw value equal to getValue()
model.table.header[0].getFormattedValue(); // returns "Month"
// To format our model Values we have two options, define a global Formatter or define a local formatter function.

// Defining a global formatter. Which returns all String in upper case.
Model.Formatter = function (value) {
    var formattedValue = value;
    if (typeof value === 'string'){
        formattedValue =  value.toUpperCase();
    }
    return formattedValue;
};

model.table.header[0].getFormattedValue(); // now returns "MONTH"

// We can also define a Formatter on a specific property which will override the global formatter.
model.table.header[0].getMetadata().Formatter = function (value) {
    var formattedValue = value;
    if (typeof value === 'string'){
        formattedValue =  "--" + value + "--";
    }
    return formattedValue;
};

model.table.header[0].getFormattedValue(); // now returns "--Month--"

/** Feature: Events **/
// Next up Events. Events are the primary mechanism that decouples the Model from the view which makes the MVC pattern so powerful. modeljs has the following events: PROPERTY_CHANGE, MODEL_CHANGE, DESTROY, CHILD_CREATED, CHILD_DESTROYED and CHANGE. Read the jsdoc for details on each event type.

// We can listen to change event by registering callbacks.
// The following are example of different ways to registar your callback

model.exampleObject.onChange(function () {
    window.console.log("exampleObject PROPERTY_CHANGE event fired");
}); // equavlant to model.exampleObject.on(Model.Event.PROPERTY_CHANGE, callback);

model.exampleObject.onChange(function () {
    window.console.log("exampleObject CHANGE (ie. PROPERTY OR MODEL) event fired");
}, true); // equavlant to model.exampleObject.on(Model.Event.CHANGE, callback); which is equavilant to registaring it on both PROPERTY_CHANGE and MODEL_CHANGE

model.exampleObject.on(Model.Event.MODEL_CHANGE, function () {
    window.console.log("exampleObject MODEL_CHANGE event fired");
}); // equavlant to model.exampleObject.on(Model.Event.MODEL_CHANGE, callback);

model.exampleObject.on(Model.Event.CHILD_CREATED, function () {
    window.console.log("exampleObject CHILD_CREATED event fired");
}); // equavlant to model.exampleObject.on(Model.Event.CHILD_CREATED, callback);

model.exampleObject.on(Model.Event.CHILD_DESTROYED, function () {
    window.console.log("exampleObject CHILD_DESTROYED event fired");
});

// Now lets do actions that fire events
model.exampleObject.anotherProperty.setValue("new Value"); // triggers a PROPERTY_CHANGE event on anotherProperty and bubbles up a MODEL_CHANGE event which triggers 2 callbacks.

// We can also trigger events manually
model.exampleObject.anotherProperty.trigger(Model.Event.PROPERTY_CHANGE);

model.exampleObject.createProperty("newProp", "newValue"); // triggers a CHILD_CREATED event
model.exampleObject.newProp.destroy(); // triggers the CHILD_DESTROYED event

/** Feature: custom Events **/
// Finally you can listen and trigger your own custom events.
model.exampleObject.on("Foo", function () {
    window.console.log("bar");
});
model.exampleObject.trigger("Foo");

// Note, any method that triggers an event will usually take a SuppressNotifications flag.
/** Feature: suppress Notifications **/
model.exampleObject.anotherProperty.setValue("Change Value but don't notify listeners", true);

/** Feature: Transactions **/
// Read the jsdoc for more information on transactions.
Model.startTransaction();

model.exampleObject.anotherProperty.setValue("set inside transaction");
model.exampleObject.anotherProperty.getValue();
model.exampleObject.trigger("Foo");
model.exampleObject.trigger("Foo");
Model.endTransaction();

// Notice transaction are fired at the end. Read jsdoc for more information about transaction options.

// That ends the modeljs tutorial. Hope that is enough to get you started. Check out the documentation and tests for other feature and examples like remote and thin models.