-
How to let users search and filter the reference data that you bind into your form
-
How to allow users of your form to call a phone number
Figure 95. When the user taps the Call button, the dialer of the mobile device pops up with the number already entered
or find a location in Google Maps directly from the form with the
link
controlFigure 96. A click-to-call and a find on map link next to the phone numbers and addresses in the data-bound table
Figure 97. If the user has Google Maps or Google Earth installed, it will start and show the address when the user taps the Find link
-
How to allow the user to make edits to the reference data by binding the reference data into user-editable controls in the form, and writing a server-side script to implement the changes in the cloud
Filter the displayed reference data in your form by user input
You'll be pulling a list of contacts from the database into the form, and adding Search by name and a Sort by functionality. You need to set up the reference table with the contacts, create the form with a dynamic list control that references the table, and set up the search and the sorting filter.
-
Fire up the text editor and create the
contact.refem
reference table declaration for the contacts list.<Reftab Name="contact" xmlns="http://schemas.mobilengine.com/reftab/v1"> <Columns> <Column Name="id" Type="Text" PrimaryKey="true" NotNull="true"/> <Column Name="name" Type="Text" NotNull="true"/> <Column Name="phone" Type="Text" NotNull="true"/> <Column Name="category" Type="Text" NotNull="true"/> <Column Name="address" Type="Text" NotNull="false"/> <Column Name="mail" Type="Text" NotNull="true"/> </Columns> </Reftab>
This is a vanilla
refem
file, with no tricky bits: theid
column needs to have thePrimaryKey="true"
attribute, so it is the ID that will identify the rows for the server-side script.A column that has the
PrimaryKey="true"
attribute declared must also have theNotNull="true"
attribute.The
address
column has aNotNull="false"
attribute, because, as you'll see in the input data, someaddress
fields are empty in the reference table . -
Save and publish the reference table declaration to generate the reference table in the Mobilengine Cloud. Download the input data spreadsheet that includes a new worksheet to populate the
contact
reference table with reference data. -
Create a new
contacts
form, set up acontactsTable
dynamic list control with thelayout="table"
attribute, and bind the reference table into the dynamic list control. Reference every column of thecontact
reference table in the dynamic list control'sgenerator
attribute. Don't forget to declare the data_type of all the columns that you reference. -
One by one, bind each of the columns of the reference table into
label
controls insidecontactsTable
. Make theid
column hidden with thevisible="false"
attribute: the user doesn't need to know about a contact's database ID.<Form name="contacts" description="Search the Contacts Database" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel" name="root"> <Control type="panel" layout="table" name="contactsTable" navigation="inline" data_type="string, string, string, string, string, string" generator="SELECT id, name, phone, category, address, mail FROM Reference_contact"> <Control type="label" name="idLabel" visible="false" reference="REF" ref_arg="PARENT.col0"/> <Control type="label" name="columnName" label="Name" reference="SELECT @1" ref_arg="PARENT.col1"/> <Control type="label" name="columnPhone" label="Phone" reference="SELECT @1" ref_arg="PARENT.col2"/> <Control type="label" name="columnCategory" label="Category" reference="SELECT @1" ref_arg="PARENT.col3"/> <Control type="label" name="columnAddress" label="Address" reference="SELECT @1" ref_arg="PARENT.col4"/> <Control type="label" name="columnMail" label="Mail" reference="SELECT @1" ref_arg="PARENT.col5"/> </Control> </Control> </Form>
-
Save the file as
contacts.form.xml
inc:\RockyJupiter
, publish, and open the form on your phone. -
Easy so far, right? Now, for the search and sort. Put an empty (
text=""
) Search by name text box and a Sort by segmented control above the dynamic list control. -
Set the
reference
attribute of the segmented control to the static querySELECT 'Name' UNION ALL SELECT 'Category'
(see the data binding guide if this seems confusing). These are the two columns of the reference table that the user will be able to sort the search results by. -
You'll need to modify the query statement in the
contactsTable
dynamic list control to make the search box and the sorting control work:-
Add a
WHERE
expression that references the value in the text box, concatenated with the % wildcard. This will make the dynamic list only return the reference data that begin with the character(s) that the user enters into the Search by name text box. -
Reference the segmented control's value in the
contactsTable
query statement to get the sorting to work: add anORDER BY
expression with a conditionalCASE
expression, that references the segmented control. -
Don't forget to reference the text box and the segmented control in the
ref_arg
attribute.
... <Control type="panel" name="root">
<Control type="textbox" label="Search by name" name="searchbox" text=""/> <Control type="combobox" name="orderBySegment" choice="button" label="Sort by" reference="SELECT 'Name' UNION ALL SELECT 'Category'"/>
<Control type="panel" layout="table" name="contactsTable" navigation="inline" data_type="string, string, string, string, string, string" generator="SELECT id, name, phone, category, address, mail FROM Reference_contactWHERE name LIKE @1 || '%' ORDER BY CASE WHEN @2 ='Name' THEN name WHEN @2 ='Category' THEN category END" ref_arg="../searchbox,../orderBySegment">
<Control type="label" name="idLabel" visible="false" reference="REF" ref_arg="PARENT.col0"/> <Control type="label" name="columnName" label="Name" reference="SELECT @1" ref_arg="PARENT.col1"/> <Control type="label" name="columnPhone" label="Phone" reference="SELECT @1" ref_arg="PARENT.col2"/> <Control type="label" name="columnCategory" label="Category" reference="SELECT @1" ref_arg="PARENT.col3"/> <Control type="label" name="columnAddress" label="Address" reference="SELECT @1" ref_arg="PARENT.col4"/> <Control type="label" name="columnMail" label="Mail" reference="SELECT @1" ref_arg="PARENT.col5"/> </Control> </Control> ...If you are new to the SQLite
CASE
expression, here's a reference. -
-
Save the file as
contacts.form.xml
in theRockyJupiter
folder, publish, and check the filtering and sorting in action.
Figure 99. The data-bound list of contacts in the form is filtered by the input that the user types into the Search by name text box
Tap the Name and Category selecters to
re-sort the search results. This form is starting to look promising.
The mobile form language lets you add controls that use the dialing and mapping applications on a smartphone to instantly call a phone number or find an address.
-
Below the
columnPhone
andcolumnAddress
labels in the form, addtype="link"
controls. -
Declare
link_type="phone_number"
on the one, andlink_type="address"
on the other. -
Use the
link_target_reference
and thelink_target_ref_arg
attributes to bind the value of thecolumnPhone
andcolumnAddress
text boxes into the link controls.... <Control type="panel" layout="table" name="contactsTable" navigation="inline" data_type="string, string, string, string, string, string" generator="SELECT id, name, phone, category, address, mail FROM Reference_contact WHERE name LIKE @1 || '%' ORDER BY CASE WHEN @2 ='Name' THEN name WHEN @2 ='Category' THEN category END" ref_arg="../searchbox,../orderBySegment"> <Control type="label" name="idLabel" visible="false" reference="REF" ref_arg="PARENT.col0"/> <Control type="textbox" name="columnName" label="Name" reference="SELECT @1" ref_arg="PARENT.col1"/> <Control type="textbox" name="columnPhone" label="Phone" reference="SELECT @1" ref_arg="PARENT.col2"/>
<Control type="link" name="phoneLink" link_type="phone_number" label="Call Contact" text="Call" link_target_reference="REF" link_target_ref_arg="../columnPhone" />
<Control type="label" name="columnCategory" label="Category" reference="SELECT @1" ref_arg="PARENT.col3"/> <Control type="label" name="columnAddress" label="Address" reference="SELECT @1" ref_arg="PARENT.col4"/><Control type="link" name="googleMapsLink" link_type="address" label="Find on map" text="Find" link_target_reference="REF" link_target_ref_arg="../columnAddress"/>
<Control type="label" name="columnMail" label="Mail" reference="SELECT @1" ref_arg="PARENT.col5"/> </Control> ... -
Save and publish the file, then check the form on your phone.
Make the data-bound values in a form user-editable
Even if you bind the reference data into user-editable controls, without a workflow script that applies the user's changes to the reference data in the cloud, the reference data will not actually change.
-
If you want the user to be able to modify the data that you bind into
contactsTable
, replace thetype="label"
controls in the dynamic list control withtype="textbox"
, except for theid
column, of course. Make thecolumnCategory
label atype="combobox"
control, because there are only a handful of available choices.... <Control type="panel" layout="table" name="contactsTable" navigation="inline" data_type="string, string, string, string, string, string" generator="SELECT id, name, phone, category, address, mail FROM Reference_contact"> <Control type="label" name="idLabel" visible="false" reference="REF" ref_arg="PARENT.col0"/> <Control type="textbox" name="columnName" label="Name" reference="SELECT @1" ref_arg="PARENT.col1"/> <Control type="textbox" name="columnPhone" label="Phone" reference="SELECT @1" ref_arg="PARENT.col2"/> <!-- The phoneLink link control was removed from this screenshot for clarity --> <Control type="combobox" choice="dropdown" name="columnCategory" label="Category" value_reference="SELECT @1" value_ref_arg="PARENT.col3" reference="SELECT 'Head Office' UNION ALL SELECT 'Colleague' UNION ALL SELECT 'Auto Service' UNION ALL SELECT 'Emergency'"/> <Control type="textbox" name="columnAddress" label="Address" reference="SELECT @1" ref_arg="PARENT.col4"/> <!-- The googleMapsLink link control was removed from this screenshot for clarity --> <Control type="textbox" name="columnMail" label="Mail" reference="SELECT @1" ref_arg="PARENT.col5"/> </Control> ...
For bonus points and extra practice, you could create a reference table that lists the Category choices that you want to display, and reference it in the drop-down. You've covered this in a previous tutorial, remember?
-
Save the file, publish it, and try it out on the phone:
-
Don't give up hope, though: server-side workflow scripts to the rescue.
You'd like a workflow script that will run on the Mobilengine Cloud every time a user
submits the contacts
mobile form, and updates the reference data that you
bound into the form based on the changes that the user makes.
-
The
server program
string in the header declares that this script will run in the Cloud, and not on your user's device.Make sure that you have
for form contacts
in the header of the script: this identifies the form (using itsname
attribute), whose submission triggers the workflow script. Your script will run as soon as one of your users submits the specified form.Double-check that you have the
using reftab contact;
line as the second line of the header - this is the reference table you will want to manipulate.The header of your workflow script
server program contactUpdater for form contacts using reftab contact;
Have you read the brief intro to workflow script anatomy yet? It's fun. Go on, we'll wait.
-
You want to cycle through the table in the form, and check if the user has modified any of the data in the rows. Just like the JavaScript for..in loop and the foreach, in statement in C#, in the Mobilengine workflow scripting language, you use the foreach statement to cycle through items in an array.
What you want to cycle through are the rows of the
contactTable
table panel in thecontacts.form.xml
form.In the workflow script syntax, you address a control in a form using a dot-separated string of the names of its ancestor controls, starting at the
<Form>
element.If, for example, you'd like to target the
contactsTable
table control in the form below, you'd use theform.root.contactsTable
expression.<
Form
name="contacts" description="Search the Contacts Database" typed="true" dateformat='(dtf yyyy"-"MM"-"dd" "HH":"mm":"ss)' numberformat='{decimalSeparator:"."}'> <Control type="panel"name="root"
> <Control type="textbox" label="Search by name" name="searchbox" text=""/> <Control type="combobox" name="orderBySegment" choice="button" label="Sort by" reference="{Query={Static {'Name', 'Category'}}}"/> <Control type="panel" layout="table"name="contactsTable"
navigation="inline" data_type="string, string, string, string, string, string" generator="SELECT id, name, phone, category, address, mail FROM Reference_contact WHERE name LIKE @1 || '%' ORDER BY CASE WHEN @2 ='Name' THEN name WHEN @2 ='Category' THEN category END" ref_arg="../searchbox,../orderBySegment"> ... </Control> </Control> </Form> -
When you've got the table-layout dynamic list control that you want the script to check, stick its address into a
foreach
statement with the iteration variablerow
to represent each of its children. In the code block statement of theforeach
, you want to update thecontact
reference table to the user input in the form:server program contactUpdater for form contacts using reftab contact; { foreach(var row in form.root.contactsTable) {
db.contact.Update({ id:row.idLabel.value}, {name:row.columnName.value, phone:row.columnPhone.value, category:row.columnCategory.value, address:row.columnAddress.value, mail:row.columnMail.value});
} }The built-in
db
variable is a map with all the reference tables that you put into the script header.Update is a function available for reference tables. It takes two parameters: a target row in the table, and the new values for the target row.
-
Save the file as
contactUpdater.rfs
, and publish the form, the workflow script, and the input data spreadsheet:c:\RockyJupiter>
mebt run . c:\RockyJupiter\reftab.xlsx
mobilengine srv 22.0 (Release) v22.0.152.14477 Executing run (compile and deploy to server): Service url: https://tutorial.mobilengine.com/services/comex/v1/ User: petar.hoyt@gmail.com adding 'contact.refem' adding 'parts.refem' adding 'vehicles.refem' adding 'contacts.form.xml' adding 'contactUpdater.rfs' Create reference table 'contact' Reset integration settings Import form contacts Import rfs contactUpdater Import reference tables Processing table 'contact' Processing table 'vehicles' Processing table 'parts' Rows added: 15, deleted: 0, modified: 0, empty: 0. Done. -
Now that you're making a server-side script that takes user-submitted input, you'll naturally be interested in what is going on in the cloud. You'll want to see, for example, if there was a problem with the workflow script or not. Log in to the Backoffice site. The Dev Console → Log screen records every single event in the cloud.
You have probably logged in to the Backoffice site before, but if you need it, here's a refresher.
-
There is no trace of your workflow script in the Log, because no
contacts
form has been submitted yet.Workflow scripts only run when a predefined event triggers them.
Make your first workflow script run: Open the form on your mobile device, modify one or two fields, submit the form, and synchronize the mobile device with the database again. Your recent changes have become part of the
contact
reference table. -
On the Backoffice site, check the Log again. A new log entry has been generated for the workflow script. Click more for the details of the script's activity.
If the script failed to run or encountered a problem while running, check the log entry for how you can sort it out.
Why this is only the beginning
You can make the user input actually have an effect on the database, which is pretty clever.
There's more to modifying a list of contacts than simply tweaking their details, though.
How about adding or deleting contacts when you feel like it? That sounds like something your users will want to do, right? Give them what they want in the next section.