What you'll be learning:
You'll leave the Rocky Jupiter scenario behind for a while to master the technique, and then get back to the photo-submitter part of the vehicle inspection form.
Data-binding is a central concept in the Mobilengine mobile form language: it is present, in one form or another, in virtually every Mobilengine mobile workflow form that you'll be building as a workflow solution developer.
The main idea behind data binding is that instead of giving a control specific value, you reference another control or a reference table with a query statement.
Because data-binding is such a key technique, it's worth looking at its various types in detail before moving on with the Rocky Jupiter vehicle inspection form.
Mobilengine data-binding is based on SQLite query syntax. You won't need to be an expert on SQLite to use it, but if you do get stuck at any point, an SQLite tutorial will definitely help.
Binding a static value into a single-value variable
At the most basic end of the spectrum, data-binding a control in a mobile form only takes
an SQLite SELECT query statement that returns a static result. Include
the query statement in the reference
attribute of the control that you bind
to the returned data.
If you put a string literal into the query, you effectively bind a static string into the control.
<Form name="example01" description="01 - Static textbox" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="textbox" name="staticBox" label="Static text data-bound into a textbox"reference="SELECT 'Hello World!'"
/> </Control> </Form>
As you can see, this form is not part of the Rocky Jupiter mobile workflow solution. Set up
a new solution artifacts folder (e.g. dataBinding
) for the data binding
tutorial, and save the code above in it.
Publish and open the form on your mobile device. You basically hard-coded a text for the text box.
Binding the value of a control into a single-value variable
A static value has its uses when you want to give a drop-down or a text box a default value instead of displaying it empty, but it's not a very exciting technique.
To make the value of a control actually depend on user input in another control, put a parameter into the query statement that will stand in for the value of the referenced control, and declare the relative address of the control that you want to link to.
The parameter will dynamically take on the current value of the control that you address in
the ref_arg
attribute.
If you reference one value, make the parameter @1
, and use
@2
, @3
, etc. if you need further placeholders. Just
remember that you need to put a relative address into the ref_arg
for each
of the parameters.
<Form name="example02" description="02 - Dynamic label" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="textbox" name="prompt" label="Enter a name" text=""/> <Control type="label" name="dataBoundLabel"reference="SELECT 'Hello ' || @1 || '!'" ref_arg="../prompt"
/> </Control> </Form>
To
make sure that the label does display NULL when you first open the
form, include the text=""
attribute on the text box.
Save the form in dataBinding
, publish, and open. Enter any string in the
prompt
text box, and watch the label change.
Binding a static value into a list
Some controls can display table values, that is, the result of a SELECT query that returns multiple rows and/or multiple columns.
In this section, your SELECT queries all use static values - you'll start querying reference tables downloaded from the cloud a bit later.
You can get multiple rows for example by combining static queries that return one row each with the UNION ALL operator. This is what you did when you set up the segmented controls in the vehicle inspection form.
Each of the SELECT
queries in the code below defines a table with one
column and one row. The UNION ALL
operator stitches them together to make
the query return a table with one column and three
rows.
<Form name="example03" description="03 - Static list" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="combobox" name="dropdown" label="Take your pick:" choice="dropdown"reference="SELECT 'Hickory' UNION ALL SELECT 'Dickory' UNION ALL SELECT 'Dock'"/>
</Control> </Form>
Save the form, then publish and open on your mobile device. This is the same hard-coding
that you did for the Hello, World!
text box, only this time the query
returns a table expression, and not a single string.
Filtering rows dynamically based on the value of a control
When you query data with an SQLite SELECT statement, you can make the query reorder, group, add to, or otherwise modify what the statement returns before the output appears in the form.
A classic example of manipulating reference data in the query is filtering the selectable values in a drop-down based on what the user enters in a text box. You can make this happen in your forms any time: you'll need to tweak the query statement a bit, but it's actually quite simple:
-
Take the query from the previous example, and filter it with a WHERE clause.
-
In the WHERE clause, use the LIKE SQLite operator to make the query return only the reference data that matches the user input in the
textbox
text box. -
To reference the text box, you need the
@1
parameter as a variable to hold the current value of thesearch
text box. You also need to specify the path to the control that you want the parameter to link to. Use theref_arg
attribute to specify the relative path tosearch
.
<Form name="example04" description="04 - Dynamic list" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="textbox" name="search" label="Filter the returned values:"/> <Control type="combobox" name="dropdown" label="Take your pick:" choice="dropdown" reference="SELECT * FROM (SELECT 'Hickory' X UNION ALL SELECT 'Dickory' X UNION ALL SELECT 'Dock' X) WHERE X LIKE @1 || '%'" ref_arg="../search"/> </Control> </Form>
Save and publish the form, then try it on your mobile device. The Take your pick: drop-down will only display the choices that start with the character or characters that you type into the text box above it.
Figure 56. The query in the drop-down uses the user input in the text box to filter the static
list in example03
Setting up dynamic list controls through data-binding
A dynamic list control is where data-binding really shines. It does not simply display the results of its query statement like other controls, but generates an instance of its template (its child elements) once for each row in the query result.
Because it needs child elements for a template, a dynamic list control needs to be a
panel
type control, but there is no other limitation. Any panel can be a
dynamic list
control.
<Form name="example05" description="05 - Static template panel" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="panel" name="templatePanel" navigation="inline"data_type="string, string" generator="SELECT 'Fuzzy' X, 'Wuzzy' Y UNION ALL SELECT 'Nitty' X, 'Gritty' Y UNION ALL SELECT 'Humpty' X, 'Dumpty' Y">
<Control type="textbox" name="generatedText" text="Yada"/> </Control> </Control> </Form>
When you reference more than one column, or a column with a non-string data type,
declare the data type of the referenced columns with a comma-separated list in the
data_type
attribute. The templatePanel
panel has
data_type="string, string"
because its query references two columns
(X
and Y
) that each hold string-type values.
Save, publish, and open the form. In this example, what the query statement returns is not important, only the number of rows it returns. (Usually, though, as in the next example, you will want to reference the values that the query returns from inside the dynamic list control.)
Figure 57. The SELECT
statements in the query each define a table that the
UNION ALL
operator combines into a single table expression
Your combined query in the generator
attribute returns a table expression
with three rows, so templatePanel
's child element gets generated three
times.
Figure 58. The dynamic list control generates an instance of its template for every row that
the query defined in the generator
attribute returns
A note about data typing
You have used the data_type
attribute in the code above, so you'll want to
know that the Mobilengine mobile form language is strongly typed. If the data type of the
values in a control is different from the default data type specified for that control, you
need to explicitly declare the data type (string, integer, boolean, etc.) of the allowed values in a control.
The default data type for most controls is string, but there are some
exceptions. In the current version of the language, the typed="true"
attribute is required on the Form
element. The dateformat
and numberformat
attributes are also required, and both are related to data
typing: these specify, respectively, how dates and numbers (number, integer and float type
values) are parsed and displayed in the form.
Referencing the results of a dynamic list control query from inside its template
The actual table that the dynamic list control above returns is not important. This is kind of strange: you normally query a reference table to display the table data itself, not just the number of rows in the reference table.
To display the actual data that the dynamic list control's query returns, you need to reference the results of the query from inside the dynamic list control template.
To do this, you use parameters (such as @1
, @2
, etc.) in
the reference
attribute, as usual. In the ref_arg
attribute, however, you need the PARENT.colK construct (where
K is the index of the column in the query resultset).
<Form name="example06" description="06 - Template panel with data-bound rows" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="panel" name="templatePanel" navigation="inline" data_type="string, string" generator="SELECT 'Fuzzy' X, 'Wuzzy' Y UNION ALL SELECT 'Nitty' X, 'Gritty' Y UNION ALL SELECT 'Humpty' X, 'Dumpty' Y"> <Control type="label" name="labelX" label="Column X"reference="SELECT @1" ref_arg="PARENT.col0"
/> <Control type="label" name="labelY" label="Column Y"reference="SELECT @1" ref_arg="PARENT.col1"
/> </Control> </Control> </Form>
Save, publish, and open the form, as usual. The query in the dynamic list control returns a table with three rows and two columns. Every row in the resultset generates the two label controls in the template once, so there'll be six labels in total. The labels in the template reference and display the values of the first and second columns of the resultset.
Figure 59. The elements in the dynamic list control template reference various columns that the dynamic list control's query returns
Both the number and the values of the labels in the dynamic list control will update
when the value that you bound into them using the @1
parameter changes.
Generating a dynamic list control by referencing the value of another control
If you reference a user-editable control in the dynamic list control's query statement to modify the returned values, you can let the user control what gets generated inside the dynamic list control.
If the reference table that the dynamic list control queries is very large, for example, the user won't have to scroll through hundreds of rows to find information. Instead, he or she can enter a few characters in a text box, and will only see the relevant rows generated.
All you need to modify in the previous form is to make the query statement of the dynamic list control look like the one in the 04 - Dynamic list form above:
<Form name="example07" description="07 - Dynamic template panel" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="textbox" name="search" label="Filter the returned values:" text=""/> <Control type="panel" name="templatePanel" navigation="inline" data_type="string, string"generator="SELECT * FROM (SELECT 'Fuzzy' X, 'Wuzzy' Y UNION SELECT 'Nitty' X, 'Gritty' Y UNION SELECT 'Humpty' X, 'Dumpty' Y) WHERE X LIKE @1 || '%'" ref_arg="../search">
<Control type="label" name="labelX" label="Column X" reference="SELECT @1" ref_arg="PARENT.col0"/> <Control type="label" name="labelY" label="Column Y" reference="SELECT @1" ref_arg="PARENT.col1"/> </Control> </Control> </Form>
Save, publish, and open the form, as usual. Note that the dynamic list control query is
filtered by the first column of the referenced tables, because the query has
...WHERE X LIKE @1...
, and X
is the name of the
first column.
Using a dynamic list control to show or hide content
A dynamic list control generates its template as many times as the number of rows that its query statement returns. So, if you set up the query statement to return 1 or 0 rows, you can make the template dynamically appear or disappear. The form below has a dynamic list control that references a checkbox: if the user selects the checkbox, the number of rows that the query returns is 1 - if not, the number of rows in the resultset is 0.
<Form name="example08" description="08 - Show/Hide Template" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="checkbox" name="showOrNo" label="Show the template?"/> <Control type="panel" name="templatePanel" navigation="inline"generator="SELECT '' WHERE @1" ref_arg="../showOrNo"
> <Control type="label" name="generatedLabel" text="Peekaboo! I see you!"/> </Control> </Control> </Form>
Save, publish, and open the form.
Select the Show the template? checkbox.
Cool, isn't it?
Back to the Rocky Jupiter folder and their vehicle inspection form. Remember: you wanted to
have the leakPhoto
photo control and the leakDescription
text box appear in the form only if the user taps the No answer in
the leaks
segmented control that asks the driver about oil leaks. The task
is basically the same as the one you just saved in the dataBinding
folder. Easy
peasy.
... <Control type="combobox" name="leaks" choice="button" label="Fuel/Oil leaks" reference="SELECT 'Yes' UNION ALL SELECT 'No'"> <Validators> <Validator type="RequiredValidator" verifyat="submit" changeindicator="both" message="Please indicate if the listed part is in order."/> </Validators> </Control><Control type="panel" name="leakSubmitter" navigation="inline" generator="SELECT '' WHERE @1 ='No'" ref_arg="../leaks"> <Control type="textbox" name="leakDescription" label="Description"/> <Control type="photo" name="leakPhoto" allowmultishoot="true" proposedresolution="1024x768" proposedquality="91"/>
</Control> ...
Study notes:
When you start binding controls to reference tables the second part of this tutorial, you'll see every type of data-binding you learned about here in action.
Save the form in the RockyJupiter
folder, and publish the solution. Open
it on your phone, and be awed: the text box and the photo control do not appear unless you
tap No in a segment control.
Figure 63. User-input reference in action: the problem-reporting controls only appear if the user selects the No option
You have come a long way, but the mobile form language still has a lot to offer -
press on to the next section.