Skip to content
BobDickinson edited this page Apr 4, 2014 · 15 revisions

Consider the sample application Counter, listed below. This application displays a count, with buttons to increment, decrement, and reset the count. The decrement and reset buttons are only enabled if the count is greater than zero. The count value is displayed in green with a normal font weight unless the count is greater than or equal to 10, in which case the count is displayed in red and bold.

// Counter page
//
var maaas = require('../maaas');

var fontStyle = 
{
    normal: { color: "Green", isBold: false },
    highlighted: { color: "Red", isBold: true }
}

exports.View =
{
    title: "Click Counter",
    onBack: "exit",
    elements: 
    [
        { control: "text", value: "Count: {count}", foreground: "{font.color}", font: { size: 24, bold: "{font.isBold}" } },
        { control: "button", caption: "Increment Count", binding: { command: "vary", amount: 1 } },
        { control: "button", caption: "Decrement Count", binding: { command: "vary", amount: -1 }, enabled: "{count}" },
        { control: "button", caption: "Reset Count", binding: "reset", enabled: "{count}" },
    ]
}

exports.InitializeViewModel = function(context, session)
{
    var viewModel =
    {
        count: 0,
        font: fontStyle.normal,
    }
    return viewModel;
}

exports.Commands = 
{
    vary: function(context, session, viewModel, params)
    {
        viewModel.count += params.amount;
    },
    reset: function(context, session, viewModel)
    {
        viewModel.count = 0;
    },
    exit: function(context)
    {
        return maaas.navigateToView(context, "menu");
    },
}

exports.OnChange = function(context, session, viewModel, source, changes)
{
    viewModel.font = (viewModel.count < 10) ? fontStyle.normal : fontStyle.highlighted; 
}

Now let's break down what is happening here. Note that for the purpose of this explanation, we will refer to the unit of functionality expressed above as a "page".

The page above provides a view, view model data, view model commands, and view model update notifications. We'll discuss how these elements fit into the application model below.

First, every page should require the maaas module. This module provides support for page navigation, user alerts, and other common functionality required by almost all pages.

var maaas = require('../maaas');

In our sample, we declare some module variables, as shown below. It is important to note that there is no module state of any kind (the module may very well even be unloaded and reloaded, and possibly even updated, during any given session). Any local module variables should be used as if they were static, as we have done here. The only state available to the module is provided via the session and viewModel parameters passed to the various callback methods.

var fontStyle = 
{
    normal: { color: "Green", isBold: false },
    highlighted: { color: "Red", isBold: true }
}

When the client requests a page, the first action taken by the Maaas server is to construct the view. This is done by retrieving the view specification from the page via exports.View, and then applying any Layout Filtering to the view.

exports.View =
{
    title: "Click Counter",
    onBack: "exit",
    elements: 
    [
        { control: "text", value: "Count: {count}", foreground: "{font.color}", font: { size: 24, bold: "{font.isBold}" } },
        { control: "button", caption: "Increment Count", binding: { command: "vary", amount: 1 } },
        { control: "button", caption: "Decrement Count", binding: { command: "vary", amount: -1 }, enabled: "{count}" },
        { control: "button", caption: "Reset Count", binding: "reset", enabled: "{count}" },
    ]
}

The next thing that the Maaas server does is retrieve the initial view model data for the page via the page's exports.InitializeViewModel method.

exports.InitializeViewModel = function(context, session)
{
    var viewModel =
    {
        count: 0,
        font: fontStyle.normal,
    }
    return viewModel;
}

Once the Maaas server has the view specification (filtered for the client, as appropriate) and the initial view model data, it sends that information to the client, and the client can then render the view and begin allowing the user to interact with it.

Various user interactions can trigger view model commands. In our example, the user clicking any of the buttons will trigger a command, as will the user activating the platform-specific "back" navigation. The view model commands are packaged together and exposed via the exports.Commands object.

exports.Commands = 
{
    vary: function(context, session, viewModel, params)
    {
        viewModel.count += params.amount;
    },
    reset: function(context, session, viewModel)
    {
        viewModel.count = 0;
    },
    exit: function(context)
    {
        return maaas.navigateToView(context, "menu");
    },
}

When the view model data is changed by the client, the server is notified via the exports.OnChange method, allowing the page module to take action based on the change, before any commands are executed. The source parameter will contain the value "view" in this case (the "view" was the "source" of the change).

When the view model data is changed by a command executing on the server, the exports.OnChange method is also called, in this case with a 'source' parameter value of "command" (a "command" was the "source" of the change). It should be noted that the command could simply apply its own post processing, but this call is provided as a convenience to allow centralized handling of view model data changes in one place. For example, in the counter sample we want to set the font based on the count without having to do this from each place that might have modified the count.

exports.OnChange = function(context, session, viewModel, source, changes)
{
    viewModel.font = (viewModel.count < 10) ? fontStyle.normal : fontStyle.highlighted; 
}

Clone this wiki locally