Client Builder User’s Guide

This is a “User’s Guide” for Client Builder. The Reference Guide for the Client Builder API may be found here.

Introduction

“Clients” are a component of Vantiq’s application development system. They are the piece that embodies an interaction with the end user, and can be used to display and collect information. A Client is constructed by a Vantiq developer using the classic MVC design strategy, which means they will create 3 different types of parts:

The “Client Builder” is the component of the Developer Portal that allows a Vantiq developer to create, edit and test a Client.

Concepts

These are some basic concepts and terminology that you should be familiar with before reading the rest of this document.

User vs. Developer

When this document uses the term “user” it is meant to refer to the end user of the Client. The term “developer” refers to the person who uses the Client Builder to create a Client. (That’s probably you, the person reading this document.)

Client

This is the component you are building that contains Models, Views and Controllers to orchestrate an interaction with the end user.

You should also note there is a single JavaScript object that is available at runtime and which provides an interface to the Client and the services it provides. (There is usually a JavaScript variable called “client” which points to it.)

Page

The Client can contain multiple “Pages”; each page corresponds to a set of Widgets and the code that responds to events they generate. Your Client always contains at least one Page which is named “Start”. The controller code you write can cause the Client to navigate between Pages. Many simple Clients will only need a single “Start” Page.

Each Page is represented at runtime by a single JavaScript object of type “Page” which provides an interface to the Page and the services it provides.

Widget

Each Page can contain various types of “View” objects which are called “Widgets”. Each type of Widget has a corresponding JavaScript class that implements it. (Some examples are “Button”, “InputString”, “DropList” and “BarChart”). Each Widget you create must have a unique name which can be used by your controller code to access the Widget at runtime.

Some Widgets may generate events when the user interacts with them, and you can write code which listens for these events and responds. (For example, Buttons have an “onClick” event.)

Some Widgets can be “bound” to data so that the Widget and data values will always be in sync. For example, if the user changes the contents of an InputString Widget, the bound data will change to match; if some of your controller code changes the data, any bound Widgets will be automatically updated to reflect it.

API

In order to build a running Client your code will often need to manipulate the Client, Pages and Widgets which comprise the Client. Taken as a whole all these objects and the services the provide are usually referred to as “the API”.

The Client Builder

The Client Builder is basically a WYSIWYG editor that allows the developer to manipulate the Client, Pages, Widgets, Data and Code which make up the Client.

Overview

Let’s discuss each of the areas within the Client Builder window and what they do.

Work Area

The scrollable area at the bottom of the window is the work area where you construct your Pages. You can drag widgets from the Palette area and drop them in the Work Area to create them. Existing widgets can be clicked and selected to reveal their property sheets, and they can dragged to move and resize them.

Button Area

In the upper-left hand corner are a set of buttons.

Overview

Save

This button will save the Client using the name you have set. All Clients must have a unique name. (You can change the Client’s name from the Client Properties popup described below.)

Run Client

You may do a test run of the Client at any time by clicking this button. When in “run” mode, all the extraneous controls from the Client Builder will be hidden. When this button is clicked, all the code you have written will be assembled into a single block and injected into the runtime environment. The client will continue to run until your codes terminates or you click the “Stop Client” button.

Reset

This button will abandon any changes you have made since the last “Save” and return the Client to its original state.

Client Area

This area concerns the properties and configurations which apply to the Client as a whole.

Overview

Client Properties

Clicking the “Properties” button will pop-up the Client property sheet.

Overview

From this popup you can change the name of the Client and edit its human-readable “description”.

“Show Grid while editing” is a checkbox that controls whether a 10x10 pixel grid will be shown in the workarea to help with positioning Widgets. (When this grid is on Widgets will automatically “snap to the grid” when you move them.)

“Mark as ‘Launchable’” is a checkbox that controls whether this Client will appear in the list of Clients shown by the “Client Launcher”. (See the Client Launcher section below for details.)

“Expert Mode” is a checkbox that controls whether certain advanced features appear in the Widget property sheets. You should leave this “off” in most cases.

Sometimes it is useful for a Client to have a “sidebar” or “topbar” which is visible on every page. Normally this will contain Menu Buttons. Clicking the “Sidebar” and/or the “Topbar” checkboxes will add these features to your Client.

Once the bar appears it can be manipulated like other containers; you can add children, move them, set their properties and change their order. (In general you will only add “Menu Button” widgets but other types of widgets can be added as well. “Menu Buttons” are just regular buttons which have been styled with a more appropriate look in a menubar.) Of course, you can add “On Click” handlers for these Menu Buttons to control what happens when the user clicks them.

Custom Code

Most of the JavaScript code you will enter will be in the form of “event handlers” which will be run when certain events occur. But it is sometimes useful to simply add your own set of JavaScript functions and global variables that will be available for use by all the event-driven code. This “Custom Code” button pops up an editor which allows you to do that; the JavaScript you enter will always be inserted as the first thing in your generated runtime code. (And of course, it must be valid JavaScript.)

On Start

Here you may add JavaScript code that will be run once when the Client first starts up; this is a good place to put any “initialization code” that will do any initial server queries or setup required by the rest of the Client. The code will be wrapped in a function like this, where the “client” parameter contains a pointer to the Client object.

//
//  This function is called when Client 'MyPageSet' first begins executing.
//
Client_onStart function (client)
{
    // Your code goes here
}
Custom Assets

In some cases you want to add 3rd party JavaScript libraries to your Client which will be available at runtime. The “Custom Assets” dialog can be used to edit the list of CSS and JavaScript assets which should be available. All the requested assets will be dynamically loaded into your Client at start time.

On Assets Loaded

Because Custom Assets are dynamically loaded they may not be available immediately. The “On Assets Loaded” event handler will be driven when all assets have completed loading. You may need to delay making use of the 3rd party code until this event handler fires.

//
//  This function is called when the custom CSS and JavaScript assets have
//  finished loading and are available for use. (Note that both 'client' and
//  'this' point to the Client object.)
//
Client_onAssetsLoaded function (client)
{
    // Your code goes here
}
Colors and Themes

Client Widgets have 4 colors by default;

You may change the values for these 4 default colors using the color selectors.

To get your started there are several custom “themes” that you may choose from that have a pre-selected set of coordinated colors.

Data Objects

The Client and all the Page objects have a JavaScript Object property called “data” which you can populate with anything you like. This “data” property is a convenient place to define global variables which persist for the life of the Client and can be used to store the working data required by your Client. This will be described in more detail in the Data Objects section below. Clicking the “Data Objects” button will pop up a dialog to let you create and edit Data Objects:

Overview

Data Streams

“Streams” are a Vantiq mechanism which allows you to define a source of data that is associated with some kind of event. The stream listens for events which carry a “message” and which the Stream in turn passes along to all its “listeners”.

In the Client Builder a Stream can be bound to various kinds of Widgets (such as Line Charts, Gauges or Data Tables) which will incorporate the emitted data into their display.

Overview

This means that Data Streams provide a “time sequenced” source of data which might be either a single object or an array of objects. The bound Widget decides how the data is to be interpreted. For example in the case of a Line Chart widget each new data item might be treated as a new point to be plotted, but a Data Table widget might completely replace the previous array of objects with the new set.

There are 5 different types of Events which can be used to drive a Stream:

“On Data Changed” Events

These events are generated by the Vantiq server when some kind of operation (insert / update / delete) occurs on a Type. The Stream will emit the object instance which was inserted or modified.

“Publish” Events

This is an event associated with doing a PUBLISH on a particular “Topic”, and is equivalent to a “WHEN PUBLISH OCCURS ON <topic> AS <message>” condition. The Stream will emit the associated message.

“Source” Events

This event is produced by a Vantiq Source and is equivalent to a “WHEN MESSAGE ARRIVES FROM <source> AS <message>” condition. The Stream will emit the associated message.

“Timed Query” Events

These are simply timer events used to run a pre-defined SELECT on a server Type at a regular interval. The Stream emits the results of the query.

“Client” Events

This allows JavaScript code within your client to use an arbitrary condition to write some data into the Stream which will be emitted to the bound Widgets.

The “Data Streams” button pops up a property sheet that allows you to define and edit the Streams required by your Client. This process will be described in more detail in the Data Streams section below.

Page Area

This area pertains to creating and editing Pages.

Overview

Switching Pages

Your Client must contain a least one Page which is always named “Start”. (Of course, this is the Page that will be displayed when the Client starts execution.) But your Client may contain more than one Page, and the “Page” droplist allows you to switch between them.

Page Properties

Clicking this button will pop up the property sheet for the currently selected Page.

Overview

The “Name” field lets you set the name of the current Page. (The Page names must be unique, and the “Start” Page may not be renamed.)

The “Default Response Topic” allows you to set the default topic associated the “response” generated by a Button. More details can be found in the Terminating with default Buttons section below.

The “On Client Start” button will pop up a dialog that allows you to edit the JavaScript code that will be run once for this Page when the Client first starts up; this is a good place to put any “initialization code” that will do any initial server queries or setup required by this Page. The code will be wrapped in a function like this, where the “client” parameter contains a pointer to the Client object. “this” will reference the Page object itself.

(To clarify, the “On Client Start” callback for a Page gets called once when the Client starts; the “On Start” callback for a Page gets called right before a Page actually becomes visible no matter how many times the Page is started.)

//
//  This function is called on page 'Start' once when the Client first begins
//  executing.
//
Client_Start_onClientStart function (client)
{
    // Your code goes here
}

The “On Start” button will pop up a dialog that allows you to edit JavaScript code that will be run every time a Page starts up; this is a good place to put any “initialization code” that must run more than once. The code will be wrapped in a function like the one below, where the “client” parameter contains a pointer to the Client object. “this” will reference the Page object itself. “parameters” will contain any optional parameters that were sent by the caller (see the ‘goToPage’ and ‘returnToCallingPage’ methods in the Client section below).

(To clarify, the “On Client Start” callback for a Page gets called once when the Client starts; the “On Start” callback for a Page gets called right before a Page actually becomes visible no matter how many times the Page is started.)

//
//  This function is called when page 'Start' first begins executing.
//
Client_Start_onStart function (client,parameters)
{
    // Your code goes here
}

Adding Pages

This button will allow you add a new Page. After creating a new Page you will probably want to use its property sheet to choose an appropriate name. Note that a Page will only appear to the user at runtime if some other Page navigates to it.

Palette Area

Overview

Creating Widgets with the Palette

You can drag from the Palette to the Work Area to add Widgets to the current Page. The new Widget will always be assigned a unique name, but you may want to change it to something meaningful.

Editing Widgets

Each Widget has its own set of properties which can be edited. All Widgets have some properties in common but each individual Widget class will generally have some properties that apply only to that particular kind of Widget. In order to edit a Widget’s properties you must select it by clicking it with the mouse. (Selected Widgets will be highlighted with a checkerboard mask.) Only one Widget may be selected at a time, so clicking a Widget will cause any currently selected Widget to be de-selected.

Property Sheets

When a Widget is selected its property sheet will pop up nearby. The property sheet is basically a 2-column list with one row per property. To edit a property you simply click on the right hand column.

Overview

Event Handlers

Many Widgets will offer Event Handlers within their list of properties. For example in the Button property sheet shown above there is an “OnClick” event. Clicking on that property will pop up a dialog that will allow you to edit the event handler. In the case of the Button:

//
//  This function is called when the user clicks the 'myButton' button.
//
Client_Start_myButton_onClick function (client,page,extra)
{
    // Your code goes here
}

The “client” and “page” parameters contain the Client and current Page objects. The “extra” parameter is usually null, but some Widgets will use it to pass extra context information about the event.

Undo, Redo, Delete

The Client Builder offers full Undo / Redo support for property changes within the workarea. (This does not apply to changes made in the Client, Page, Data Object and Data Stream property sheets, just the Widgets themselves.)

A Widget which has been selected can be removed with the “Delete” button, and the “Undo” button will put it back.

Views

The “Views” portion of your Client is made up of the Widgets which are placed on a Page. The widgets can be roughly grouped into 3 categories: Control Widgets, Data Stream Widgets and Layout Widgets.

Control Widgets

These are in general the standard “controls” that most users will be familiar with (buttons, input fields, droplists, etc.) as well as some special “viewer” controls. (When clicked the viewers usually open a separate window to display some resource.) Some of these have the ability to bind to a property of a Data Object. (For example, you would might bind an InputString widget to a String property in the Data Object of the current Page.)

Data Stream Widgets

These widgets are typically for display only; they are connected to a Data Stream and will update themselves to response to new data arriving on the Stream. (For example, a line chart would draw new data points and shift the older data to the left.) More details on these widgets can be found below in the Widgets and Streams section.

Layout Widgets

For simple Page layouts you can manually give each Widget a position on the Page. But sometimes it is useful to put the Widgets inside a “container” widget which assigns positions to its children using some algorithm. Generally if the size of the children changes for some reason the parent will automatically adjust the positions of its children to compensate.

For example, in the image below there are 2 VerticalLayout containers; both contain 2 children (a PieChart and some StaticText to act as a title). Note that in both cases the title has been automatically centered by the VerticalLayout even though the PieCharts are different sizes. Used this way containers can sometimes save time and effort in producing a pleasing layout. Layout containers can be nested and configured to produce many kinds of layout arrangements.

Note that in this example the borders are left in for clarity but can be turned off if you don’t want to see them.

Overview

There are 4 kinds of layout containers available:

FlowLayout

This container allows you to explicitly set the container’s width and the children will be arranged left-to-right and top-to-bottom in a “flow” layout (similar to the way a browser “div” lays out its children). The height of the FlowLayout will be adjusted to fit the children but the width stays fixed.

GridLayout

In a GridLayout you set an explicit number or rows and columns which creates a fixed number of “cells”. Each cell can be empty or accommodate a single child. This is often useful for creating a classic “form” layout.

Overview

HorizontalLayout

In a HorizontalLayout the children are laid out left-to-right in a horizontal row.

VerticalLayout

In a VerticalLayout the children are laid out top-to-bottom in a vertical columm.

Class Hierarchy

The Widgets are arranged in a class hierarchy which is shown below; note that only “leaf” classes can be instantiated.

Controllers

The “Controller” components of a Client are the JavaScript fragments provided by the developer to implement the actual behavior. Most of this is expressed as “Event Handlers”; that is, you define the body of a function that will be called when certain conditions occur. This can be things like:

There is one exception to this; it is sometimes useful to simply supply your own set of JavaScript functions and global variables that will be available for use by all the event-driven code. This is known as “Custom Code” and is added using the Client Property Dialog. For example you might want to define various utility functions that could be called from an event handler. (Any function or data you define as “Custom Code” is available globally.)

Event handler functions will usually have a predefined set of parameters that will be available when they are called. Usually there will be a “client” and “page” parameter set since it is often useful to have them available.

In the case of handlers defined on Widgets (such as Button “On Click”) the “this” variable will be pointing to the object (in this case the Widget) to which the event applies.

On Start Events

One important use for the event handlers is to initialize your Client and Pages in some way. At startup time the runtime system will always call the Client “On Start” handler before anything else happens. It will then call the “On Client Start” handlers for every Page you have defined. Note that these startup events will only happen once no matter how many times you may enter or leave a Page.

Each Page may also have an “On Start” handler as well; these will be called before the Page actually starts executing. The difference is that if you navigate away from the Page and then back again the Page’s “On Start” handler will be called again.

Your Client can be written so that everything happens on a single Page. But it is often useful to divide your Client into multiple Pages and then navigate between them when the situation requires it. There are two functions defined on the Client object that are used for this.

It’s simplest to explain this with an example. Suppose your Client has two Pages: “Start” and “ShowDetails”.

On the “Start” Page you add a button that has the label “Show Details” and whose “On Click” handler looks like this:

//
//  This function is called when the user clicks the 'showDetailsButton' button.
//
Client_Start_showDetailsButton_onClick function (client,page,extra)
{
    //
    //  Navigate to the "ShowDetails" Page and pass some parameters. The parameters 
    //  will be available in the "On Start" handler for the "ShowDetails" Page.
    //
    client.goToPage("ShowDetails",{x:1});
}

The second parameter of the “goToPage” method (“{x:1}”) offers a way to pass a parameter to the target page; it will passed as “parameters” argument in the “ShowDetails” page’s “onStart” callback.

//
//  This function is called when the user clicks the 'doneButton' button.
//
Client_ShowDetails_onStart function (client,parameters)
{
    // "parameters" contains the optional argument in the "goToPage" call
}

The “ShowDetails” Page might have another button defined with the label “Done” and whose “On Click:” handler looks like this:

//
//  This function is called when the user clicks the 'doneButton' button.
//
Client_ShowDetails_doneButton_onClick function (client,page,extra)
{
    //
    //  Navigate back to whichever Page called us and pass some parameters. The parameters 
    //  will be available in the calling Page's "On Start" handler.
    //
    client.returnToCallingPage({someParms:1});
}

Server Requests

Another common task for your Controller code will be to interact with the Vantiq server via the REST API. Such requests are handled using a special class called Http. This example shows how you might do a sorted query for all the records of Type “MyType”.

    //
    //  Create an instance of the Http class to execute our server request
    //
    var http = new Http();

    //
    //  Build the URL needed to do a "select" on our Type
    //
    http.setVantiqUrlForResource("MyType");
    //
    //  Add the Authorization header to the request
    //
    http.setVantiqHeaders();

    //
    //  Specify the (optional) query parameters
    //
    var queryParameters = {
        sort: {name:1}
    };

    //
    //  Execute the asynchronous server request. This expects 3 parameters:
    //
    //  queryParameters: "null" or an object containing the parameters for this request
    //  successCallback: A callback function that will be driven when the request completes
    //                   successfully (i.e. a status code of 2XX)
    //  failureCallback: A callback function that will be driven when the request does not complete
    //                   successfully.
    //
    http.select(queryParameters,function(response)
    {
        console.log("SUCCESS: " + JSON.stringify(response));

        //
        //  At this point "response" is an array containing the objects returned for the "select"
        //
    },
    function(errors)
    {
        //
        //  This call will format the error into a popup dialog
        //
        client.showHttpErrors(errors,"Doing a select on 'MyType'");
    });

More detailed information on crafting a REST API request can be found in the API Reference Guide. In particular the “parameters” mentioned below are the standard “query object” which is described here.

There are variations of the “http.select” request for all the obvious server operations. Note that in all cases you must call http.setVantiqUrlForResource(<resourceTypeName>) first to establish the Type or source you are targeting.

aggregate(pipeline:any[], successCallback:Function, failureCallback:Function):void;

Returns all the resources instances of the Type for an aggregation pipeline.

select(parameters:any, successCallback:Function, failureCallback:Function):void;

Returns all the matching resources instances of the Type.

selectOne(resourceId:string, parameters:any, successCallback:Function, failureCallback:Function):void;

Returns a single instance of the Type identified by <resourceId>.

insert(data:any, parameters:any, successCallback:Function, failureCallback:Function):void;

Inserts a new instance of the Type (<data>)

update(data:any, resourceId:string, successCallback:Function, failureCallback:Function):void;

Updates an existing instance of the Type (identified by the <resourceId>) using the supplied <data>

upsert(data:any, successCallback:Function, failureCallback:Function):void;

Upserts a new instance of the Type (<data>)

deleteAll(parameters:any, successCallback:Function, failureCallback:Function):void;

Remove all instances of the Type which match the <parameters>

deleteOne(resourceId:string, parameters:any, successCallback:Function, failureCallback:Function):void;

Deletes a single instance of the Type identified by <resourceId>.

patch(resourceId:string, patchInstructions:any[], parameters:any, successCallback:Function, failureCallback:Function):void;

Patches a single instance of the Type identified by <resourceId> and using <patchInstructions>

publish(data:any, topicOrSource:string, successCallback:Function, failureCallback:Function):void

Publishes the JSON payload in <data> using the topic or source name in <topicOrSource>. You must have previously called http.setVantiqUrlForResource(“sources”) or http.setVantiqUrlForResource(“topics”) for this to work.

execute(procedureArguments:any, procedureName:string, successCallback:Function, failureCallback:Function):void

Execute a procedure with the supplied name and arguments. You must have previously called http.setVantiqUrlForSystemResource(“procedures”) for this to work.

query(parameters:any, resourceId:string, successCallback:Function, failureCallback:Function):void

Perform a query against the Source named <resourceId>. You must have previously called http.setVantiqUrlForResource(“sources”) for this to work.

Models

The “Model” portion of a Client refers to the data that it operates on. Of course you can always create JavaScript variables directly in your code, but the Vantiq Client “Model” also includes 2 other components called “Data Objects” and “Data Streams”.

Data Objects

As described in the section above, each Page and the Client itself have a property called “data” which points to a Data Object. It is up to the developer to declare the properties that these objects contain.

For example, if you click the “Data Objects” button in the Client Builder you will see a dialog like this:

Overview

The dropdown at the top of the dialog lets you select which Data Object you are editing. For a brand new Client with only a single Page you will see these 2 Data Objects:

All of these Data Objects start off empty, containing no properties. It is up to the developer to populate them. Note that properties defined with the “client.data” Data Object are effectively “global variables” since the “client” object can be accessed from any page. In general a Page should not refer to a different Page’s Data Object.

Select the “page.data for page ‘Start’” item (if it is not already selected) and we will add some properties.

Let’s assume that you are trying to implement a Client that calculates the area of a rectangle; we will need properties for “height”, “width” and “area”. Click the “Add Property” button three times to add 3 new properties to this Data Object and fill them in like this:

Overview

Each row defines a property of the “Start” Page’s data object. The first column is the property name; it must be a legal name for a JavaScript variable. The second column is a droplist that lets you set the property’s type. (Note these are the same scalar datatypes used by the Vantiq server. There is one special exception called “Typed Object” which will be discussed below.) The “Default Label” column is optional; it’s a hint that the system can use to provide a more human-readable label in the UI. “Default Value” is also optional. When a Client starts up all the Data Objects you have defined will be instantiated, and these are the values the property will be assigned. Click the “Save and Exit” button to return to the main Client Builder.

Once these properties have been defined you can reference them from your code. For example, you might create a Button on the “Start” page with an “On Click” handler like this:

//
//  This function is called when the user clicks the 'areaButton' button.
//
Client_Start_areaButton_onClick function (client,page,extra)
{
    page.data.area = page.data.width * page.data.height;
}

Most of the event handlers will pass the value for the “client” and the current “page” to you as arguments.

So far all we have done is compute the result and save it in a Data Object. The Data Objects are also useful if you want to edit or display the values as well.

From the Client Builder we can use the palette to create 3 “Real” number input widgets. For each one we can “bind” it to a value in a Data Object by setting the “Data Binding” property to “page.data.height”, “page.data.width” and “page.data.area” respectively. When you run the Client and click the button the area will be calculated and the resulting “page.data.area” value will appear in the bound widget. If you modify the values in the widgets which are bound to “height” and “width” the corresponding page.data values will be updated automatically. Similarly if your code changes the value in a Data Object any “bound” widget will reflect the change.

Data Objects can contain any kind of scalar value (“Integer”, “String, “GeoJSON”, etc.). But they can also contain a special type called a “Typed Object”. This is just like a regular JavaScript object except that it also maintains metadata on all the properties the Typed Object contains. It knows the property’s name and datatype as well as a default value (which will be assigned at start time) and a default label (which could be used when generating a UI for the property.)

These Typed Objects could be made manually of totally arbitrary properties or could correspond to the definition of a Type in the Vantiq server. The Client Builder can build these Types for you by using the Type’s schema in the Vantiq server.

For example, suppose you have a Type defined in the server called “Employee” which has 4 properties:

Property Datatype
name String
age Integer
salary Integer
mgrName String

We could manually build a Data Object property of datatype “Typed Object” and populate it to match the definition in the server. But it is easier to let the Data Object dialog do it for you. First use the “Choose Type” droplist to select the Type in the server (“Employee” in this case).

Overview

Next click the “Add a ‘Typed Object’ based on Type:” Button; a Typed Object property called Employee will be added to the Data Object:

Overview

Click the “pencil” (Edit) Action icon and we will “drill in” to see the details of this “Employee” property:

Overview

You can see that a property was created to correspond to each of the properties defined in the “Employee” type. Of course you are free to update any of the property metadata items (such as to give the properties a better “Default Label”.) Now click the “Cancel” button to pop back up to the previous display (or “OK” if you made changes). From there we can click the “lightning bolt” (Generate Widgets) Action icon to cause a basic set of widgets to be created. Click “Save and Exit” to see what the generator did:

Overview

This little form was built by creating a GridLayout with 2 columns and as many rows as needed to accommodate the properties in the Data Object. (You will probably want to move it to a suitable position on the Page.) Each Input widget was given an appropriate “Data Binding” property to connect it to its corresponding property in the page.data.Employee Data Object.

An obvious use for this group of generated Widgets would be to display the results of a query that returns a single instance of the “Employee” type. For example you might put this code inside the “On Click” event of a Button marked “Run Query”:

    //
    //  Create an instance of the Http class to execute our server request
    //
    var http = new Http();

    //
    //  Build the URL needed to do a "select" on our Type
    //
    http.setVantiqUrlForResource("Employee");
    //
    //  Add the Authorization header to the request
    //
    http.setVantiqHeaders();

    //
    //  Specify the (optional) query parameters
    //
    var queryParameters = {
        where: {name:"Michael"}
    };

    //
    //  Execute the asynchronous server request. This expects 3 parameters:
    //
    //  queryParameters: "null" or an object containing the parameters for this request
    //  successCallback: A callback function that will be driven when the request completes
    //                   successfully (i.e. a status code of 2XX)
    //  failureCallback: A callback function that will be driven when the request does not complete
    //                   successfully.
    //
    http.select(queryParameters,function(response)
    {
        console.log("SUCCESS: " + JSON.stringify(response));

        //
        //  At this point "response" is an array containing the objects returned for the "select"; assign
        //  it to the page.data.Employee Data Object so that the bound widgets will show the results.
        //
        page.data.Employee = response[0];
    },
    function(errors)
    {
        //
        //  This call will format the error into a popup dialog
        //
        client.showHttpErrors(errors,"Doing a select on 'Employee'");
    });

Summary

This may help visualize the way the Client, Pages and Data Objects are related in memory:

Overview

There is always a single “Client” object which is usually available to your code in as a “client” variable. The Client object has a property called “data” which points to the “global” data object. (i.e. “client.data”). The “getCurrentPage()” method on the Client will always point to the currently active Page. (But there is usually a “page” variable provided for you in the Event handlers that points to it as well.)

There is one “Page” object for each Page you have defined in your client (and there will always be a least one Page called “Start”). In general there is always a JavaScript variable called “page” provided which points to the current page. Each Page object has a property called “data” which points to that Page’s Data Object (i.e “page.data”).

A Data Object may contain zero or more properties. The properties may be scalars (Integer, Real, String. etc.) or a “Typed Object”. Typed Objects can contain properties of their own; in this example “page.data.Employee” points to a Typed Object which contains the properties name, age, salary and mgrName.

Data Streams

“Data Streams” are a mechanism which allow you to define a source of real-time data that is associated with some kind of event. (You should review the discussion of Data Streams in the section above.)

Once a Data Stream has been defined and is producing data there are various kinds of widgets which can “listen” to the incoming stream and display it in some way. These are all the Widgets which inherit from the DataStreamWidget class:

You can visualize a Data Stream as a black box that periodically emits one or more objects. That stream of objects can be fed to a Widget which will extract data from the objects in some way and incorporate it into some kind of visual display.

Widgets and Streams

Different Widgets will interpret this flow of objects differently; there are 3 general categories: For some Widgets each burst of data will be appended to a growing set of time-sequenced values. Others simply extract one or more values from the last object that arrived. And some take each burst as a complete replacement for any previous data.

Append Objects to an ever-growing set

The three most obvious examples of this category are the Line Chart:

Overview

the Bar Chart:

Overview

and the Column Chart:

Overview

Each time a new data object arrives the data is extracted and slides in from the right. (There is a limit and once points become old enough they disappear off the left edge.)

The ListWidget simply displays the last “n” values for a single property; old values roll off the bottom:

Overview

The PieChart does something slightly different; as each new value arrives it counts how many of each value has been seen and displays this histogram as a Pie Chart:

Overview

The DynamicMapViewer extracts unique objects and their locations from the incoming data and display it against a map. As new points come in they update the positions of the objects and the markers move around the map:

Overview

The FloorplanViewer does the same thing but the points are plotted over a “floorplan image” saved with the widget:

Overview

Extract a value from the most recently arrived object

These widgets have no history, they just extract a value from the most recent object.

The NumberViewer is designed to act as an “infographic” that just displays a single important number:

Overview

The Gauge is similar except the value is displayed like this:

Overview

Complete Replacement using the latest update

This final category takes each burst of data as a complete set of data which replaces whatever might have been there before. A common use for this would be to use a DataTable with a “Timed Query” Stream to show the complete set of results from some query:

Overview

Defining Streams

To create and edit Data Streams from the Client Builder you click on the “Data Streams” button which will pop up a dialog like this:

Overview

There are no Streams defined in your Client by default so you must create one. Click the “New Data Stream” button and you will see the Data Stream editor dialog. The first thing you will want to do is to enter a name for the new Data Stream. Remember there are 5 different types of Data Streams; when creating a new one it will default to the “On Data Changed” Stream. In the sections below we will look at how you create a Stream for all 5 types.

“On Data Changed” Streams

This Stream emits data whenever an insert, update or delete operation is done against the specified Type. The emitted data is the target record itself.

The “Group By” field is optional and is only required by certain types of Widgets such as the FloorplanViewer. It specifies which property of the target Type is used divide the data into groups. (See the Image Map Tutorial for details.)

Overview

“On Publish Event” Streams

This Stream listens for the event associated with doing a PUBLISH on a particular “Topic”, and is equivalent to a “WHEN PUBLISH OCCURS ON <topic> AS <message>” condition in a Rule. (The Stream will emit the associated message.)

You must enter the topic that triggers the event. Note that there is no schema for the associated message (since it is not an instance of a server Type.)

The “Group By” field is optional and is only required by certain types of Widgets such as the FloorplanViewer. It specifies which property of the target Type is used divide the data into groups. (See the Image Map Tutorial for details.)

Overview

“On Source Event” Streams

This event is produced by a Vantiq Source and is equivalent to a “WHEN MESSAGE ARRIVES FROM <source> AS <message>” condition in a Rule. (The Stream will emit the associated message.)

You must select the Source that generates the messages. Note that there is no schema for the associated message (since it is not an instance of a server Type.)

The “Group By” field is optional and is only required by certain types of Widgets such as the FloorplanViewer. It specifies which property of the target Type is used divide the data into groups. (See the Image Map Tutorial for details.)

Overview

“On Timed Query” Streams

These are simply timer events used to run a pre-defined SELECT at a regular interval. The Stream emits the results of the query.

You must select a server Type first. The “Update Interval” must be a non-negative number indicating the delay in seconds before the query is run again. (If this value is “0” the query will be run once and never again.)

The “Group By” field is optional and is only required by certain types of Widgets such as the FloorplanViewer. It specifies which property of the target Type is used divide the data into groups. (See the Image Map Tutorial for details.)

By default the entire query result will be returned. If you click the “Limit maximum number of records to return” checkbox it will reveal some additional fields that will allow you set a limit and select the property to sort on (since setting a maximum isn’t very useful without sorting the data first.)

Overview

Once a Type has been selected you are allowed to set some simple constraints on the query (the “where” clause). These constraints are “and”ed together.

Overview

If the simple constraints are not sufficient you can click the “Use Advanced Query” checkbox, allowing you to specify the “where” clause as a JSON object. (For more details on constructing this object you can consult the API Reference Guide)

Overview

“On Client Event” Stream

This allows JavaScript code within your client to use an arbitrary condition to decide when to write some data into the Stream which will be emitted to the bound Widgets. In order for the data that is emitting from this Stream to have a Schema you must specify a server Type or a global Data Object (such as “client.data.Employee”).

Your JavaScript code will use some criteria to determine when to cause the Stream to emit data and then call the “sendClientEvent” method. “theObject” must be an object that matches the schema you configured into the Data Stream.

client.sendClientEvent("MyDataStream",theObject);

Overview

Binding Data Streams to Views

In order to bind a widget to a Data Stream you simply select it and set the relevant properties from the property sheet.

A simple example of this can be seen if you select a Gauge - there are properties to select the Data Stream the Gauge should be bound to (“Data Stream”) and which property should be used to extract the relevant value (“Data Stream Property”). Note that if the Data Stream is of the “Publish” or “Source” variety then there is no schema available you and will be asked to type in the name of the property which contains the value the Gauge should display. For the other Data Streams (which do have a schema) you will see a dropdown list that will allow you select from the list of appropriate properties.

Overview

Other widgets such as the LineChart may be a little more complicated. In this case you must select the “Data Stream” but then you will need to select which properties should be used to extract values for the x-axis and y-axis data. (“X-Axis Property” and “Y-Axis Properties”.) Since a LineChart can plot more than one value at a time you are allowed to specify more than one “y-axis” property value. (Again, schema-less Data Streams will require you to enter property names explicitly rather than select them off a list.)

Overview

Terminating the Client

Clients are launched from inside some other environment. At present than means one of three things:

This means there will probably be some sense in which the Client’s work is “done” and you want to return to the environment which launched you. (When running from the Client Builder or Client list there is also a “Stop Client” button which can be used to abort the running Client.) In any case there needs to be a way for the Client to declare that it has “terminated” and the calling environment should take control again.

Explicit Termination

You can explicit ask to terminate the client directly from your code with the Client “terminate” method like this:

client.terminate();

Terminating with default Buttons

If you add an InlineButton or FooterButton you may add your own “On Click” event handler which can do whatever it likes, including calling the “terminate” method. However Buttons which have no event handler have a special default behavior which is similar to the way Buttons operate in the RCS Page Editor.

In this default case the Buttons will first generate a “response object” by collecting data from all the ControlWidgets on the current Page. It will then do a PUBLISH using the current Page’s “default response topic” (set from the Page’s property sheet) and using the generated response object as a payload. (The “submit value” for the button will be automatically added into the payload object so the receiver can tell which button was clicked). Finally the button will call “client.terminate()” to terminate the app.

The code below is the JavaScript equivalent of this behavior:

//
//  This is equivalent to the default behavior for a button which has no "On Click" handler
//
Client_Start_someButton_onClick function (client,page,extra)
{
    var responseObject = client.createResponseObject(this.submitValue);
    var responseTopic = page.responseTopic;

    var http = new Http();

    //
    //  Build the URL needed to do a publish on a topic
    //
    http.setVantiqUrlForSystemResource("topics");
    //
    //  Add the Authorization header to the request
    //
    http.setVantiqHeaders();

    http.publish(responseObject,responseTopic,function(response)
    {
        client.terminate();
    },
    function(errors)
    {
        //
        //  This call will format the error into a popup dialog
        //
        client.showHttpErrors(errors,"Doing PUBLISH on a topic");
    });
}

Uploading data to the Server

A common purpose for Clients is to collect input data from the user and then upload it to the server where it can be used to drive Rules, Procedures and Collaborations. This input data comes in two different flavors -

Default Upload Behavior

If your Client contains a Button widget with no “onClick” handler it is assumed that you want the “default upload behavior” - this means that all the Scalar data from the current Page will be automatically loaded into a “response Object”. Any media widgets from the current Page will upload their data into Documents and save the generated names into the “response Object” as well. That Object will be attached to a “publish” whose target topic comes from the default value for the Page. The Client will automatically exit (and the Notification will be removed from the “inbox”).

Customized Uploads

In many cases this default behavior is not sufficient - perhaps your Client wants to add some custom data to the response object or does not want the Client to terminate. In this case you can override the Button’s onClick handler and specify the exact behavior you want.

Here is sample code that emulates the default upload behavior described above. You could paste this into the onClick handler of a Button and the Client would behave exactly the same as the default:

    //
    //  Initialize a response object from the Scalar and Media data on the current Page
    //
    var responseObject = client.createResponseObject(this.submitValue);
    var responseTopic = page.responseTopic;

    var http = new Http();

    //
    //  Build the URL needed to do a publish on a topic
    //
    http.setVantiqUrlForSystemResource("topics");
    //
    //  Add the Authorization header to the request
    //
    http.setVantiqHeaders();

    http.publish(responseObject,responseTopic,function(response)
    {
        //
        //  Terminate the Client
        //
        client.terminate();
    },
    function(errors)
    {
        //
        //  This call will format the error into a popup dialog
        //
        client.showHttpErrors(errors,"Doing PUBLISH on a topic");
    });

You can use this sample as a template for customizing the behavior. For example, you could add your own values to the responseObject before the http.publish, or remove the client.terminate() to prevent the Client from exiting.

A slightly more advanced approach makes use of a special “Uploader” object which allows you to take total control of the upload process. Again, you would probably add code like this into the onClick handler of a Button:

    //
    //  Initialize an Uploader object
    //
    var uploader = new Uploader(client);

    //
    //  Add Media data from a Camera Widget
    //
    uploader.addRequestFor("Camera3");

    //
    //  Add Media data from a VideoRecorder Widget
    //
    uploader.addRequestFor("Video1");

    //
    //  Build a custom responseObject with any values you like
    //
    var responseObject = {
        "a":1,
        "b":2
    };

    //
    //  Declare the response object and the topic which should be user with the publish
    //
    uploader.setResponseObject(responseObject,"/capture/payload");

    //
    //  Start the Uploader - this will upload the Media data into Documents and publish 
    //  the responseObject to the topic.
    //
    uploader.start(function(theUploader)
    {
        //
        //  Upon completion the Widgets will contain the name of the target Documents where
        //  the media was saved.
        //
        var cameraWidget = client.getWidget("Camera3");
        console.log("DocumentName=" + cameraWidget.documentName);
    });

See the Reference Guide here for more details.

The Client Launcher

The Client Launcher is an app that allows you to launch Clients without using the Dev Portal and the Client Builder environment. Normally you would launch the Dev Portal with a URL like this:

https://dev.vantiq.com/ui/ide/index.html

The Client Launcher uses a slightly different URL:

https://dev.vantiq.com/ui/rtc/index.html

It has a very simple interface: it shows a list of all the Clients which have been marked as “Launchable” in the Client property sheet. (See the Client Properties section above.)

Overview

Simply click on the Client to launch it. When the client terminates it will return to this list.

Debugging

Before a Client starts execution all of your callback and custom code will be assembled into a file called “ClientCallbackEvents.js” and injected into the HTML page so the functions can be called when needed. During development you can use your browser’s built-in debugger to see this code at runtime and set breakpoints in the usual manner.

One of the drawbacks of JavaScript development is that often the runtime environment will “eat” any errors and exceptions, making failures very mysterious. The Vantiq Client execution environment will try to catch all exceptions in your code and display them in a popup dialog before terminating the client.

For example, suppose you create a Client which contains only a single InlineButton like this:

Overview

The Button is given a name “crashButton” with a label of “Boom”. The “On Click” event handlers is set to this:

//
//  This function is called when the user clicks the 'crashButton' button.
//
Client_Start_crashButton_onClick function (client,page,extra)
{
    var a = null;
    a.boom();
}

Obviously this code will throw an exception if the “Boom” button is clicked. When that happens the exception will be trapped and turned into a popup dialog showing the error like this: (note the details will vary between browsers; this example was done using Chrome.)

Overview

After the user clicks the “OK” button the Client will terminate.

There are clues in the call stack that tell us what happened. First is the error message itself:

Cannot read property 'boom' of null

The top line of the error stack tells us where it happened:

at PIInlineButton.Client_Start_crashButton_onClick (ClientCallbackEvents.js:8:3)

The generated function name for a widget event handler will have a form like

Client_<page name>_<widget name>_<event name>

So we can assume that the problem happened on the “Start” page in the “OnClick” event of the “crashButton” widget.

You could also use your debugger to load “ClientCallbackEvents.js” and set a breakpoint inside the offending method and investigate the problem that way.