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": "<..>"
}]
}