dataform

The dataform bundle allows the creation of form widgets based on JSON definitions.

Usage

If you have installed the development version of map.apps, you can use the test page for evaluating the capabilities of the bundle.

Defining dataforms in JSON

A dataform is defined by a JSON object with properties in a hierarchic structure, that determine how each control is aligned in the form, what data it holds and how it looks like. Each definition has a root control that is located at the top of the hierarchic structure. This root control element must always have the property "dataform-version" : "1.0.0". The root control may contain several child controls.

For example, the root control can be a layout container for its child controls. The following code sample shows how to define a root control with a height of 200 pixels and a width of 500 pixels.

{
    "dataform-version": "1.0.0",
    "type" : "panel",
    "size": {
        "h": 200,
        "w": 500
    },
    "children": [{
        "type": "label",
        "value": "Text (max: 10)",
        "size": {
            "l": 5,
            "t": 20
        }
    },
    {
        "type": "textbox",
        "field": "text1",
        "max": 10,
        "size": {
            "l": 150,
            "t": 20,
            "w": 200
        }
    }]
}

This control has two children. The first one is a label control and the second one is a textbox. For the label the value property specifies the label's text. The textbox has a max property that sets the maximum number of characters for the textbox to 10.

More importantly, the field property defines the name of the property in the object binding, that this control is bound to. The object binding is the data object, that is passed to the createBinding method when the binding is created. For example, given a slider control with its field property set to the value age. When the user sets the value of the slider to 31, the age property in the object binding is set to that value, too (in this case 31).

To ensure that large dataform definitions do not block the UI thread, add the flag "create-async":true to a dataform definition:

{
    "dataform-version": "1.0.0",
    "create-async" : true,
    "children": [{ ...}]
}

Use Cases

How to use dataforms in your own bundle

To use the dataform bundle in your own code, inject the DataFormService component into a custom component. Add the following reference to your component definition in the manifest.json file of your bundle:

"references": [{
    "name": "dataformService",
    "providing": "dataform.DataFormService"
}]

In the JavaScript class of your component you can then create a data form as described in the following code sample:

// DataformService injected by the component runtime
var dataformService = this.dataformService;

// dataformDefinition is the JSON definition of the datafrom.
var dataformDefinition = {...};
var dataformWidget = dataFormService.createDataForm(dataformDefinition);

// place dataform widget in DOM
var domNode = ...
dataformWidget.placeAt(domNode).startup();

var state = {
  a: "property a",
  b: "property b"
};
// Binds "plain" JS object to data form.
// This state is edited by the data form controls in the ObjectBinding.
var binding = dataFormService.createBinding("object", { data: state });

dataformWidget.set("dataBinding", binding);
// watch for changes in feature
binding.watch("*", function(name, oldVal, newVal) {
	// called when a property is changed in the object
});

// get state back from ObjectBinding
var newstate = binding.data;
...

How to register custom controls

The best way to register custom controls is to register a ControlFactory.

A ControlFactory component in the manifest.json file:

{
    "name" : "MyControlFactory",
    "provides" : "dataform.ControlFactory",
    "properties" : {
      // the new type name
      "+dataformtype" : "mycontrol"
    }
}

The implementation of MyControlFactory may look like this:

define([    
    "dojo/_base/declare", 
    
    // base class of all controls
    "dataform/controls/_Control",
    
    // the dijit widget to display
    "./MyWidget"
], function(declare, _Control, MyWidget) {
    
    var MyControl = declare([_Control], {
        controlClass: "mycontrol",
        
        // create the concrete dijit widget
        createWidget: function(params) {       
            return new MyWidget(params);
        },
        
        // called if a databinding is removed
        clearBinding: function() {
            this._updateValue(this.field);
        },
        
        // called if new databinding is provided
        refreshBinding: function() {
            var binding = this.dataBinding;
            var field = this.field;
            var widget = this.widget;
            
            this.connectP("binding", binding, field, "_updateValue");
            this.connectP("binding", widget, "value", "_storeValue");
            
            this._updateValue(field, undefined, binding.get(field));
        },
        
        _updateValue: function(prop, oldVal, newVal) {                
            this.widget.set("value", newVal);
        },
        
        _storeValue: function(prop, oldVal, newVal) {
            if (this.widget.isValid()) {
                this.dataBinding.set(this.field, newVal);
            }
        }
    });
    
    return declare([], {
    
        // a ControlFactory needs only to implement this method
        createFormControl: function(props) {                    
            return new MyControl(props);
        }
    });
});

Control Reference

The following user controls are provided by the bundle:

Type Description
accordionpanel A layout panel displaying subpanels as accordion stack
borderpanel A layout panel arranging subpanels with "top,right,bottom,left,center" properties
button A simple Button that the user can click. When clicked a map.apps framework event is fired
checkbox A form control which displays a checkbox, used for boolean conditions
colorpicker A widget for choosing a color from a gradient, or specify it by setting RGB, Hex, or HSV values
combobox A widget similar to a select box, but it is possible to add values not present in the list
datetextbox A text input form control for editing date values, provides a calendar for selecting dates
gridlayoutpanel A layout widget that allows the use of layout grid classes for responsive design
gridpanel A layout widget that allows to align child widgets in a tabular layout
image An image widget
label A form control displaying static text
numberspinner A text input form control for editing numbers and providing +/- buttons
numbertextbox A text input form control for editing numbers
panel A layout panel for placing other panels or form controls
radiobutton A form control which displays a RadioButton. It allows to group different RadioButtons to a single Group where only one RadioButton can be active.
selectbox A drop-down widget that allows the user to choose a value from a predefined list of values
slider A control for choosing a value from a range of values by moving a slider knob
stackpanel A layout widget, which shows only one child at the time
switch A control for setting a field to true or false
tablepanel A control, which produces a table
tabpanel A layout panel displaying subpanels as tabs
textarea A text input form control for multiline text
textbox A text input form control
timetextbox A text input form control for editing time values, provides a drop down for time selections
togglebutton A button that works similar to a checkbox. When clicked it returns a value of true, and false otherwise
uploader A file upload control to load files from the client to the server
validationicon An icon which allows the control of the validation state
wizardpanel A layout widget that aligns its child widgets on multiple pages, which can be navigated using "Next" and "Previous" buttons

Common properties

A control definition has the following common structure and a common set of properties:

{    
    // required
    "type": "<the type of the control>",

    // optional, it depends on the parent widget, how this is interpreted
    "title": "Title 1",
    
    // optional, it creates a tooltip which is displayed if the user hovers with the mouse over the control.
    "tooltip": "My Tooltip",

    // optional, only valid on input controls, marks a control as not editable.
    "disabled": true

    // optional, binds the control to a specific data element, valid for input controls.
    "field" : "<a name in the data binding>",
    
    // optional, defines that the input control is required.
    "required": <true|false>,

    // optional, defines a css class/ or classes (separated by space)
    "cssClass": "<custom css class name>",

    // optional, should rarely be used(only if static layouts are required)
    // it defines the size and position of a control, relative to its parent.
    "size":{
        // optional, the width
        "w" : 300,
        // optional, the height
        "h" : 300,
        // optional, left position
        "l" : 5,
        // optional, top position
        "t" : 5,
        // optional, right position
        "r" : 5,
        // optional, bottom position
        "b" : 5
    },
	
    // optional, only valid for layout widgets, which may have children.
    "children": [{
        // 1. child
	},{
        // 2. child
    }]
}

accordionpanel

A layout panel displaying subpanels as accordion stack.

{
    "type": "accordionpanel",
    
    "children": [{
        // any other layout widget
        "type": "<...>",
    
        // title is used as accordion label
        "title": "Title 1",
    
        // marks this panel as the first displayed accordion element
        "selected" : true,
    }]
}

borderpanel

A layout panel arranging subpanels with the region property. Allowed values are top, right, bottom, left, center.

{    
    "type": "borderpanel",

    // optional, can be 'headline' or 'sidebar', defaults to 'headline'
    // defines if top and bottom are above left and right (headline) or between (sidebar).
    "design" : "headline",
	
    // required, child panels which define the region contents.
    "children": [{
        // any layout widget
        "type": "<...>",

        // required, can be top,left,right,bottom,center, this is the region 
        // interpreted by the border panel to correctly place the panel.
        // center gets all remaining space. 
        // All other region panels have to specify a size according to its region flag (w or h).
        "region": "center"
    }]
}

button

A control which displays a button.

{
    "type": "button",

    // title, text written inside button
    "title" : "My Button",

    // optional, allows binding to a data field to control the disabled state
    "disabledField": "a-field",

    // required to add code to listen on the button click
    "topic": "mybutton/Click"
}

How to react on a button click:


var dataformWidget = dataFormService.createDataForm(...);

dataformWidget.on("ControlEvent",function(e){
    if(e.topic === "mybutton/Click"){
        // do something
    }
});

checkbox

A control which displays a checkbox, used for boolean conditions.

{    
    "type": "checkbox",

    // required: binds the control to a data field
    "field": "user.role",

    // optional, defines the value set on the field if selected
    "value": "role1",

    // optional, defines if a not selected value should be set on the field
    "uncheckedValue" : "",
    
    // optional, allows binding to a data field to control the disabled state
    "disabledField": "a-field"
}

colorpicker

Shows a color picker to choose a color value. You can configure whether the color picker should always be shown completely or as a drop down button.

{
    "type": "colorpicker",
    "field": "colorValue",

    // Shows HSV selection range
    "showHsv": true,  
    // Shows the rgb selection range
    "showRgb": true,
    // Shows the color picker inside a drop down button.
    "asDropDown": true
}

combobox

A widget similar to a select box, but it is possible to add values not present in the list.

{
    "type": "combobox",

    "field": "myfield",
    
    // define that a value have to match a regular expression
    "regex": "[0-9]{5}$",

    // Do not react on drop-down browsing
    "updateOnValueChange": false,

    // search a store:

    // optional: which field in the store is searched
    "searchAttribute": "id",
    // optional: how should the labels be displayed
    "labelAttribute": "${title}(${id})",
    // optional: which store should be searched
    "store": "storeId",
    

    // predefine a list of values
    // optional: define values
    "values": [
        "Lowest",
        "Low",
        "Normal",
        "High",
        "Highest"
    ]
}

datetextbox

Displays a textbox, that when clicked, opens a calendar widget to choose a date. You can use this control together with the timetextbox control to select a date and a time as a single combined value that is written to the data binding. For this, both the datetextbox and the timetextbox need to have the same value for the field attribute and for both controls, the bindingFormat attribute has to be set to "unix".

{
    "type": "datetextbox",

    // required: binds the control to a data field.
    "field": "startDate",

    // optional: highest allowed date 
    "max" : "2012-05-12",
    
    // optional: lowest allowed date
    "min" : "2012-05-01",
 
    // The format of the date value that is used in the binding. "unix" means 
    // that the date value represents the number of milliseconds since 
    // January 1st, 1970, 00:00. If the bindingFormat is not specified, the 
    // date is represented as an ISO string.
    "bindingFormat": "unix"    
}

gridlayoutpanel

A layout widget, which organizes its children as responsive grid.

This control requires deeper knowledge about responsive grid layouts, like described at Grid-View.

This widget helps to create the required div structure with less json code.

{    
    "type": "gridlayoutpanel",

    // how many children each row
    "cols": 2,
    
    // each widget cell is dived into a label cell and value (widget cell)
    "showLabels": true,

    // optional, defines the col css class written on each cell.
    "cellClass": "col-md-6",

    // optional, defines the col css class written on a label cell.
    "labelClass": "col-sm-5",

    // optional, defines the col css class written on a value cell.
    "valueClass": "col-sm-7",

    // optional, defines that colspan are used to recalculate teh cellClass col values
    // for example col-md-6 is calculated to col-md-12 if colspan is 2.
    "calculation": true,

    "children": [{
        // any other widget
        "type": "<...>",

        // title is used to create a label cell
        "title": "Title 1",
        
        // optional, defines that the cell spans more than one column
        "colspan" : 2,

        // optional, true means that the label cell is not created
        "spanLabel": false,

        // overwrite default settings from parent:
        "cellClass": "col-md-6",
        "labelClass": "col-sm-5",
        "valueClass": "col-sm-7",
        "calculation": false
    }]
}

gridpanel

A layout widget, which organizes its children as a table using floating div elements.

{    
    "type": "gridpanel",
    
    // how many children each row
    "cols": 2,

    "children": [{
        // any other widget
        "type": "<...>",

        // title is used to create a label cell
        "title": "Title 1"

        // optional, defines that the cell spans more than one column
        "colspan" : 2,

        // optional, true means that the label cell is not created
        "spanLabel": false,
    }]
}

image

An image control.

{    
    "type": "image",
    
    // optional, field contains image src
    "field": "imagesrc"

    // optional, directly image URL
    "src": "<URL to image>"
}

label

A form control displaying text.

{
    "type": "label",

    // optional, defines the value of this label, allows binding to multiple fields, by {..} expressions in the text.
    "value": "Shows {field1} and {filed2}",

    // optional: binds the label to a data field.
    "field": "title"
}

numberspinner

A text input form control for editing numbers and providing +/- buttons.

{    
    "type": "numberspinner",	
    
    "field": "name",

    // optional: highest allowed number
    "max" : 10,

    // optional: lowest allowed number
    "min" : 10,

    // optional: (default: true) defines if decimal numbers(true) or integers (false)
    "fractional" : false,

    // optional: (default: undefined) defines count of allowed fractional digits can be number or string.
    // 0 === fraction:false,
    // "3" means exactly 3 fractional digits
    // "0,3" means 0 till 3 fractional digits
    "places" : "0,3",

    // optional (default: 1): defines increment used at spinner clicks.
    "inc" : 3
}

numbertextbox

A text input form control for editing numbers.

{
    "type": "numbertextbox",

    "field": "name",

    // optional: highest allowed number
    "max" : 10,

    // optional: lowest allowed number
    "min" : 10,

    // optional: (default: true) defines if decimal numbers(true) or integers (false)
    "fractional" : false,

    // optional: (default: undefined) defines count of allowed fractional digits can be number or string.
    // 0 === fraction:false,
    // "3" means exactly 3 fractional digits
    // "0,3" means 0 till 3 fractional digits
    "places" : "0,3"
}

panel

A layout panel for placing other panels or form controls. For positioning children, size information or css definitions are required.

{    
    "type": "panel",

    // optional, any other control
    "children": [...]
}

radiobutton

Radiobuttons are grouped automatically according to their field. To use one field for each radiobutton individually, provide a groupId, which works like the HTML name attribute.

{
    "type": "radiobutton",
    
    "field": "planeType",
    
    // optional, defines the value set on the field if selected    
    "value": "Sportsplane",       

    // defines, a group, multiple radio may share this, so that all others are unselected
    "groupId": "group1"
}

selectbox

A select box is a special type of combo box that only allows predefined values as input (values).

{
    "type": "selectbox",

    "field": "myfield",
    
    //control if the selectbox should be searchable. If false, a combobox is rendered.
    "searchable": false,
    
    // define that first value should automatically be selected
    "useFirstValueAsDefault": true,

    // do not react on drop-down browsing
    "updateOnValueChange": false,

    // search a store:

    // optional: which field in the store is searched
    "searchAttribute": "id",
    // optional: how should the labels be displayed
    "labelAttribute": "${title}(${id})",
    // optional: which store should be searched
    // the store must define property "useIn": ["dataform"] in
    // order to be accessible by the dataform widget
    "store": "storeId",
    

    // predefine a list of values
    // optional: define values
    "values": [
        "Lowest",
        "Low",
        "Normal",
        "High",
        "Highest"
    ]
}

slider

A slider control to 'slide' number values.

{    
    "type": "slider",
    "field": "sliderValue",    
    
    // lowest value
    "min": 1,
    
    // maximal value
    "max": 100,
    
    // how much steps between min and max
    "discreteValues": 100,
    
    // should the slider be labeled (shows the ruleLabels)
    "showRuleLabels": true,
    // should ticks be shown on the slider
    "showRule": true,
    // labels for the slider
    "ruleLabels": [
       "low",
       "high"
    ]
}

stackpanel

A panel which allows switching of the child to display.

{
    "type": "stackpanel",

    // a number data field, which defines which child to display
    "field": "selectedIndex",

    "children": [{
        // first child (index 0)
        // any layout control
        "type": "<...>",

        // marks this child as the child to display
        "selected" : true
    },{
        // second child (index 1)
        "type": "<...>"
    }]
}

switch

A switch control.

{    
    "type": "switch",

    // required: binds the control to a boolean data field
    "field": "afield"
}

tablepanel

A layout widget, which organizes its children as table.

{    
    "type": "tablepanel",
    
    // how many children each row
    "cols": 2,

    "children": [{
        // any other widget
        "type": "<...>",

        // title is used to create a label cell
        "title": "Title 1"
    }]
}

tabpanel

A layout panel displaying subpanels as tabs.

{    
    "type": "tabpanel",

    "children": [{
        // any layout control
        "type": "<...>",

        // title is used as tab label
        "title": "Title 1",

        // marks this panel as the first displayed tab
        "selected" : true
    }]
}

textarea

A text input form control for multiline text.

{    
    "type": "textarea",

    // required: binds the control to a data field.
    "field": "name",

    // optional: maximal amount of chars allowed in this text field
    "max" : 10,

    // optional: defines regular expression to which the user input must match.
    // this is a multiline input field, your regular expression must match the whole text, also line separators (\n and \r)
    // e.g: only lines of numbers
    "regex": "(\\d+[\\n\\r]{0,2})+",

    // optional (default: false): enables the html5 textarea resize handle, which is disabled by default.
    "resizeable" : true
}

textbox

A text input form control.

{    
    "type": "textbox",
    
    "field": "name",
    
    // optional: When set to "password" the textbox displays the text with concealed characters.
    "textboxType": "password",
        
    // optional: maximal amount of chars allowed in this text field
    "max" : 10,

    // optional: defines regular expression to which the user input must match.
    // e.g: 5 numbers
    "regex" : "[0-9]{5}$"
}

timetextbox

A text input form control for editing time values. It provides a drop down button for selecting a certain time. You can use this control together with the datetextbox control to select a date and a time as a single combined value that is written to the data binding. For this, both the datetextbox and the timetextbox need to have the same value for the field attribute and for both controls, the bindingFormat attribute has to be set to "unix".

{
    "type": "timetextbox",

    "field": "startTime",

    // optional (default is local dependent): defines the time display pattern.
    "pattern" : "HH:mm:ss",

    // optional: highest allowed time, if no timezone specification is given, the time is user browser specific. 
    "max" : "T12:00Z",

    // optional: lowest allowed time, if no timezone specification is given, the time is user browser specific.
    "min" : "T10:00Z",

    // optional: increment steps of the time selection drop-down.
    "inc": "T00:00:10"
}

togglebutton

A control which displays a toggle button, used for boolean conditions.

{    
    "type": "togglebutton",

    // displayed inside the button
    "title": "A Role",

    // required: binds the control to a data field
    "field": "user.role",

    // optional, defines the value set on the field if selected
    "value": "role1",

    // optional, defines if a not selected value should be set on the field
    "uncheckedValue" : ""
}

uploader

An upload control. Requires a special multiform upload backend.

{
    "type": "uploader",

    // binds the uploader to a datafield, in which the filename is written
    "field": "fileName",

    // which file patterns to accept
    "accept": [
       ".png",
       ".jpg",
       ".jpeg"
    ],

    // special css class to highlight the uploader as "drop down zone".
    "cssClass": "dropZoneUploader",
    
    // label inside the upload button
    "value": "Scotty, load me up!",
    
    // label after successful upload
    "uploadedLabel": "I', uploaded",
    
    // should CORS width credentials be enabled?
    "useCredentials": true,
    // URL to the backend
    "url": "$raw/someImage.png"
}

validationicon

Displays a small icon reflecting a validation state.

{
    "type": "validationicon",
    
    // the data field which controls the validation state
    // should be a number:
    // -1 = do not show the icon
    // 0 = invalid
    // 1 = valid
    // 2 = processing (spinner)
    "field": "valid",

    // hide the control independent of the state
    // required to control the validation state internally
    "show": true
}

wizardpanel

A layout control to navigate step by step through children.

{
    "type": "wizardpanel",
    "children": [{
          // any layout panel
          "type": "<..>",
          "selected": true,
       },{
          "type": "<..>"
    }]
}