You'll be learning about data-binding in Mobilengine forms:
This section is a detour of sorts. For a short while, you'll be leaving the exciting world of Rocky Jupiter to concentrate on data-binding itself, without any use case to distract you. When you get back to the admin webforms, you'll be prepared to streamline the Vehicle Inspection and jazz it up with a host of cool new features.
Data-binding is an essential feature of the Mobilengine form language. It is perhaps the single most useful technique a Mobilengine developer such as yourself can have.
The basic idea is that instead of hard-coding a specific value for a control, you put a reference into the control (aka bind the control to) to one or more properties of another control, to a reference table, or to a system parameter, and pull data into your control from them. You want to do this primarily because the data-bound control will always be up-to-date - its value changes in real time when the referenced resource changes
Data-binding is based on SQLite query syntax, with the most important difference being that unlike SQLite, it is strongly typed, which allows compile time type-checking. You won't need to be an expert on SQLite to use data-binding in Mobilengine solutions, but if you do get stuck at any point, an SQLite tutorial will definitely help. For specifics, see the comprehensive data-binding reference.
Binding a static value into a single-value property
First things first: since the examples below don't form part of the Rocky Jupiter scenario,
you need a separate artifacts folder to hold them. Create a new folder and name it
dataBinding
.
The most basic type of data-binding is a query statement that returns a static result.
In the Mobilengine form language, query statements are {wrapped in curly brackets}. Put a
string {"between quotes inside braces"}, declare it as the text
property of
a display control, and you've got yourself a data-bound
control.
Save
the code inside the dataBinding
folder, and publish through the
mebt
as usual, then open the form on the webforms website.
Static query results have their uses when you want to give a drop-down or a text box a
default value, but you could just as well hard-code a string into the control. There is much
more to data-binding than this.
Binding the value of a control into a single-value property
You can make the value of a control actually depend on the user input in another control by referencing the user-editable control in the query statement. The data-bound control will dynamically, and in near-real-time, take on the value of the referenced control.
All this takes is sticking a reference to the control in question into the query statement
of the control you'd like to data-bind, using dot notation:
{
referenced-control-id
.
referenced-property-name
}
.
When referencing other controls in the form, you need to be aware of the controls' naming scopes. Read up on the concept in the form language reference.
Save, publish, and open this form on your mobile device. Enter words into the text box, and
see them appear in the textview
control below it.
Displaying a control conditionally
This is the type of data-binding you've seen in the previous tut: wrapping controls in an if
with a condition that references a
value in control in the form. The child elements of the if
will only appear
in the form if the condition evaluates to true.
It works just the same as the previous examples, except that the data-binding expression that is the condition must evaluate to a boolean.
Data-binding
is strongly typed so you need to convert the string value text
attribute to an integer before you can compare it to
another integer.
Save, publish, and open in your device. Lie about your age if you must to make the age-restricted text appear.
Figure 57. The text below is only displayed if the input in the textbox
meets
the condition that you specify
Binding more than one static value into a control
If you'd like to reference and display a list of values or the rows and/or columns of a
table instead of a scalar value, you'll want to put something in the query statement that
returns a table value: a reference table, a table
control's
rows
property, or a table expression.
For this data-binding 101 you'll be using this last one, then the penultimate one, and finally graduate to querying reference tables in the next chapter. A table expression or table literal is a static collection of columns and rows stitched together. You can use dynamic query statements in the table expression 'cells', as long as they return a scalar value.
TABLE column1, column2, column3 (row1column1, row1column2, row1column3; row2column1, row2column2, row2column3; row3column1, row3column2, row3column3)
Figure 58. Table expression syntax
Include as many columns and rows as you need. Note the TABLE
keyword
that sets up the expression, and the semicolons that separate the rows inside the expression
body.
Now for the really interesting part. You can't just stick a query that returns a table into
a control that can only display scalar values (like a textview
). You need a
special type of control that can resolve table values. The repeater
in this example takes
a table literal that returns two columns and four rows, and a template in the form of child
elements. Then, it generates the template for each row of the returned table value; meaning
there will be four textbox
controls in the
form.
<form id='example04' menuName='Data-binding example #4' platforms='ios' xmlns='http://schemas.mobilengine.com/fls/v2'><repeater id='crash' recordset='{table column1, column2 ("First", "Anteater"; "Second", "Barracuda"; "Third", "Cockatoo"; "Last", "Dog")}' record='r'>
<textbox label='{"This is the " ||r.column1
|| " generated textbox."}' text='{r.column2}'
/> </repeater> </form>
Generating the child controls via data-binding is one thing; the real point is that you can
reference the rows returned in the repeater
from inside its template, using
the record
iterator variable. The iterator variable cycles through each of
the returned rows in turn, and you can use the
{
iterator-variable
.
column-name
}
query to pull the value from each of the fields in a given row of the query result one by
one. The example code above references one of the returned columns in the
label
, and the other in the text
attribute of the
generated controls.
Seeing is believing. Save the form, publish it, and open it on your mobile.
Figure 59. The number of textbox
controls is determined by the number of rows
the query in the repeater
returns, and their text
and
label
attributes reference the values in its columns
Filtering rows dynamically based on the value of a control
If the table returned in a repeater
's recordset
has a lot
of rows, you may very reasonably want to filter them based on user input. This is quite
easily done.
Since it's the query statement in recordset
that determines what can be
displayed in the row template, this is what you need to fiddle with to make filtering
happen.
All you need is some conventional SQLite query syntax: a SELECT ... FROM
statement that includes a
WHERE
clause and the
LIKE
clause to return only the rows that match
the input in a user-editable
control.
05_repeaterTableExFilter.fls.xml
<form id='example05' menuName='Data-binding example #5' platforms='ios' xmlns='http://schemas.mobilengine.com/fls/v2'> <textbox id='filterbox' label='Filter the animals by their names' text='{""}'/> <repeater id='crash' recordset='{SELECT t.column1 FROM (table column1 ("Anteater"; "Barracuda"; "Cockatoo"; "Dog")) t WHERE t.column1 LIKE filterbox.text || "%"}
' record='r'> <textview label='{"The animal that starts with the letter " || UPPER(filterbox.text) || " is"}' text='{r.column1}' /> </repeater> </form>
The
textview
control in the repeater
simply references the
rows returned by the query in the parent using record
, just like in the
previous example.
Save, publish, and open the form to test the behavior. Neato.
Figure 60. The query statement in the repeater
filters the static
recordset
based on the input in the textbox
Just like the SQLite database language it is based on, Mobilengine data-binding includes functions to process the query results inside a query statement. Query functions are like the formulas in Microsoft Excel that count, average, round down or up, or make various calculations on a range of values in the spreadsheet.
Read the exhaustive reference on all the query functions supported by Mobilengine data-binding for details.
The data-binding expression in the textview
below the
repeater
in the code below, for instance, references the string input in
the generated textbox
es in the repeater
, as an ordered
list of strings. You can reference the controls in the template using the rows
read-only
property, present on controls that have a recordset
:
control-id
.rows.
table-alias
06_repeaterTableExProcess.fls.xml
<form id='example06' menuName='Data-binding example #6' platforms='ios' xmlns='http://schemas.mobilengine.com/fls/v2'> <textview text='Your three favorite colors?'/> <repeater id='crash' recordset='{table column1 (""; ""; "")}' record='r'> <textbox id='color' hint='Type a color'/> </repeater> <textview text='{SELECT UPPER(GROUP_CONCAT (t.color.text, "$")) FROMcrash.rows
t}'/> </form>
The code above doesn't in fact reference the values of the recordset
in
line 7 - the number of rows it returns is all that matters in this example.
The UPPER
function converts the strings in the list that the query
statement returns to uppercase, and the GROUP_CONCAT
function joins them up
with the separator you specify as the second parameter ("$"
here).
Save, publish, and open. Enter text in the textbox
es to test the query.
Modify the GROUP_CONCAT
query function to change the separator character to
whatever you fancy. Have fun.
Figure 61. The textview
at the bottom uses functions in its query statement
to convert the list of user input strings in the generated textbox
es to
uppercase, and then to concatenate them using the $
character
You might have noticed that the repeater
control in that last example had
a table literal that had but a single static column. If you've got a list of scalar values
to represnt in the form, the data structure you need is a list, not a table. Learn how lists
work in the Mobilengine world now.
Lists are comma-separated scalar values or scalar-value expressions [in square brackets],
and you can set them up as a recordset
property without a care in the
world.
As
you can see, there is something unexpected after all. Since a list doesn't have columns,
hence, no column names, what column-name
are you supposed to use in
the
{
iterator-variable
.
column-name
}
syntax to reference the parent repeater
's query in the template?
Automatic column alias assignment applies when a data-binding expression returns a single un-named column
(a list can be seen as a one-column table in SQLite terms). v
is the
default column alias in these cases.
Save the form, publish, and open on your device.
Setting up a data-bound drop-down list control
The complete data-binding section has been building up to the drop-down list, and here it
is. A dropdown
is a typical candidate for data-binding: the whole point of this
type of control is to present dynamically updating options. The Mobilengine form language
implementation of the control, however, is slightly more involved than you'd expect.
The choices
property, which specifies the list of options in a drop-down
is a query that binds to a reference table, a table expression, or a table-value property:
no problem there.
The drop-down, however, needs two more query statements that reference the query result in
choices
: one that returns a (scalar or record) value that identifies the
options that the user can select. This is called keyMap
. The other query
specifies the options that the drop-down presents to the user (aka the text of the choices).
This is the textMap
property.
Nearly always, keyMap
and textMap
return a column of the
table that you reference in
choices
.
<form id='example08' menuName='Data-binding example #8' platforms='ios' xmlns='http://schemas.mobilengine.com/fls/v1'> <dropdown id='dropdown1' label='Pick your weapon:'choices='{TABLE column1, column2, column3, column4 (1, "Rock", "BEATS: Lizard, Scissors", "LOSES TO: Spock, Paper"; 2, "Paper", "BEATS: Rock, Spock", "LOSES TO: Lizard, Scissors "; 3, "Scissors", "BEATS: Lizard, Paper", "LOSES TO: Spock, Rock"; 4, "Lizard", "BEATS: Spock, Paper", "LOSES TO: Rock, Scissors"; 5, "Spock", "BEATS: Scissors, Rock", "LOSES TO: Lizard, Paper")}' keyMap='{column1}' textMap='{column2}'
/> <textview label='Selected key:' text='{TOSTRING(dropdown1.selectedKey)}'/> <textview label='Selected text:' text='{dropdown1.selectedText}'/> <textview label='Selected value:' text='{TOSTRING(dropdown1.selectedValue.column1) || " - " || dropdown1.selectedValue.column2 || " - " || dropdown1.selectedValue.column3 || " - " || dropdown1.selectedValue.column4}'/> </form>
You can reference the user selection in a drop-down in a number of ways, demonstrated in the code above:
Save, publish, open, and find out about your options. You've got the first steps to building a simple game in a form right here.
Figure 63. The drop-down list displays the rows in its static recordset as options. The controls below the drop-down reference various fields of the recordset row that the user selected
Now that your data-binding muscles are pumped, you're ready to revisit the Rocky
Jupiter Vehicle Inspection form, and show it who's boss.