In a dynamic list control, you only need to declare a template for the
generated child elements. The dynamic list control will generate the template that
you set up inside for each row returned by the query specified in its
generator
attribute.
You've already coded dynamic list controls in the data-binding tutorial.
In the previous tutorial,
you linked the choices for a drop-down control to reference data. Now, you'll bind
the popup panels in vehicleInspection.form.xml
to the
parts
reference table.
Because these panels have label and segmented control child elements, this will make the number and values of the labels and the number of segmented controls that appear in the form dependent on the reference data in the table.
The segmented controls have a static query (the text Yes and No), so the reference data that their parent links to will not determine their value, only how many will be generated.
In this section of the tutorial, you'll slowly but surely make the contents of the whole form dependent on a single reference table with the help of two dynamic list controls nested into each other, and some clever reference data filtering.
When you are done, the parts of the truck, the groups that they appear in in the
form, and the optional parts that the driver can report about will come from the
parts
reference table, and the form will automatically update
the controls and control values whenever the data in the reference table change.
To get the ball rolling gently, you'll first learn how to turn one of the three popup panels in your form into a dynamic list control, so that the car parts that are listed inside it come from a list of car parts in the Rocky Jupiter database.
-
Create the reference table declaration for the
parts
reference table that you'll bind into the Front of Vehicle popup panel. Save the declaration insidec:\RockyJupiter
asparts.refem
. -
With the
mebt
, publish the folder to the Mobilengine Cloud to generate the reference table.c:\RockyJupiter>
mebt run . reftab.xlsx
mobilengine srv 22.0 (Release) v22.0.157.14908 Executing run (compile and deploy to server): Service url: https://tutorial.mobilengine.com/services/comex/v1/ User: petar.hoyt@gmail.com adding 'optionalParts.refem' adding 'parts.refem' adding 'vehicleInspection.form.xml' No changes for reference table 'optionalParts' Create reference table 'parts' Reset integration settings Import form vehicleInspection Import reference tables Processing table 'optionalParts' No changes Processing table 'parts' Rows added: 7, deleted: 0, modified: 0, empty: 0. Done. -
Create (or download) the input data spreadsheet that will populate the reference tables with data. As you can see in the screenshot below, it has a few extra parts to make it obvious that the popup panel has been bound to the reference table.
-
Now to the form. You'll make the
popupFront
panel show the yes/no controls dynamically based on the data inparts
.... <Control type="panel" name="popupFront" navigation="popup" label="Front of vehicle">
<Control type="label" name="frontLabel" text="Front of Vehicle"/> <Control type="panel" name="partsFront" navigation="inline" generator="SELECT part_name FROM Reference_parts"> <Control type="label" name="frontSegmentedLabel" reference="SELECT @1" ref_arg="PARENT.col0"/>
<Control type="combobox" name="frontSegmented" choice="button" 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="submitter" navigation="inline" generator="SELECT '' WHERE @1 ='No'" ref_arg="../frontSegmented"> <Control type="textbox" name="submitterDescription" label="Description"/> <Control type="photo" name="submitterPhoto" allowmultishoot="true" proposedresolution="1024x768" proposedquality="91"/> </Control></Control>
</Control> ...The
partsFront
panel on line 4-8 is a dynamic list control: it has agenerator
attribute.The
"SELECT part_name FROM Reference_parts"
query inside thegenerator
attribute returns all the rows fromparts
, and the template in the dynamic list control (lines 9-36 in the code sample above: thefrontSegmentedLabel
label and thefrontSegmented
segmented control, the validator control for the segmented control, and thesubmitter
panel and its child elements) is generated in thepartsFront
dynamic list control once for every row that the query returns.The
frontSegmentedLabel
label is data-bound to the first (and, in this case, only) column that its parent, the dynamic list control, returns:ref_arg="PARENT.col0"
. Notice that all the other, static child elements insidepartsFront
have been deleted.If you need a refresher about
ref_arg
syntax when referencing columns in a query resultset, re-read the relevant part in the data-binding overview.The
frontLabel
label is a sort of breadcrumb that repeats the label of the popup panel inside the popup itself. Put it outside the dynamic list control - otherwise, it would be generated above each of the generated child elements. -
Save the file as
vehicleInspection.form.xml
insideRockyJupiter
, publish (referencingreftab.xlsx
in themebt
), and check:
Generate the child elements of all three vehicle region popups
This experiment of generating the contents of a popup panel worked out nicely. Let's repeat the process for the other two popup panels.
First, you'll need to merge the static data inside the popupRear
and
popupEngine
popup panels with the parts
reference table data. Then you add a second column to filter the relevant car parts
into the three separate popup panels.
-
A second column? That means modifying the declaration.
Save the declaration in
c:\RockyJupiter
, and publish it to the Cloud to create the reference table for the popups. -
Download the input data spreadsheet that has the merged data and indicates in the
where_in_vehicle
column which popup panel the car part in question needs to appear in. -
Modify the form to link all three popup panels to the reference data.
Just like you did with the
popupFront
panel, you'll makeRear of Vehicle
andEngine
dynamic list controls that generate their template based on a query statement.However, this time, change the query statement in their
generator
attribute so that it only returns the rows that have a particular value in thewhere_in_vehicle
field.... <Control type="panel" name="popupRear" navigation="popup" label="Rear of vehicle">
<Control type="label" name="rearLabel" text="Rear of Vehicle"/> <Control type="panel" name="partsRear" navigation="inline" generator="SELECT part_name FROM Reference_parts WHERE where_in_vehicle='Rear of Vehicle'"> <Control type="label" name="rearSegmentedLabel" reference="SELECT @1" ref_arg="PARENT.col0"/>
<Control type="combobox" name="rearSegmented" choice="button" 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="rearSegmentedSubmitter" navigation="inline" generator="SELECT '*' WHERE @1 ='No'" ref_arg="../rearSegmented"> <Control type="textbox" name="rearSegmentedDescription" label="Description"/> <Control type="photo" name="rearSegmentedPhoto" allowmultishoot="true" proposedresolution="1024x768" proposedquality="91"/> </Control></Control>
<Control type="panel" name="miscellaneousControl" navigation="inline" layout="standard" count="0"> <Control type="combobox" choice="dropdown" name="miscellaneousControlElementDropdown" text="Car part" reference="SELECT part_name FROM Reference_optionalParts"/> <Control type="textbox" name="miscellaneousDescription" label="Description"/> <Control type="photo" name="miscellaneousPhoto" allowmultishoot="true" proposedresolution="1024x768" proposedquality="91"/> </Control> <Control type="button" name="miscellaneousAddButton" text="Add part"> <Actions> <Action type="generator" target="../miscellaneousControl" removebuttontext="Remove"/> </Actions> </Control> </Control> ...This code sample declares
popupRear
only. You will need to change the code for the two other popup panels as well for the form to work.The template for all three dynamic list controls looks the same:
The main thing is the
WHERE where_in_vehicle=''
expression inside the query statement of the dynamic list controls. Make sure that it is different for all three, so that each dynamic list control only pulls the relevant reference data from the reference table. -
Save the file as
vehicleInspection.form.xml
in the solution artifacts folder, publish it referencing the newreftab.xlsx
spreadsheet, and check that everything in the form looks fine.
Generate the vehicle region popups with a dynamic list control as well
What if Rocky Jupiter's policy changes, and drivers have to
inspect, say, the Undercarriage
of the trucks as well? You, their
developer, would have to come back to the
vehicleInspection.form.xml
file, and set up a fourth popup
panel.
To really automate updating the form whenever the reference data changes, you need to get rid of these three separate popup panels that you have to label by hand separately.
This is actually quite easily done: you need to nest the dynamic list control that generates the child elements of the popup panel inside another dynamic list control.
...<Control type="panel" name="popupGenerator" navigation="inline" generator="SELECT DISTINCT where_in_vehicle FROM Reference_parts"> <Control type="label" name="popup" reference="SELECT @1" ref_arg="PARENT.col0"/>
<Control type="panel" name="wrapper" navigation="popup"> <Control type="label" name="popupLabel"reference="SELECT @1" ref_arg="../../popup"
/> <Control type="panel" name="popupContent" navigation="inline" generator="SELECT part_name FROM Reference_parts WHERE where_in_vehicle=@1
"ref_arg="../../popupHeader
"> ... </Control> </Control> ...
-
Instead of three popup panels, this code only has one (
popupContent
), wrapped in thepopupGenerator
dynamic list control. Thegenerator
query statement inpopupGenerator
referencesparts
, but hasDISTINCT where_in_vehicle
, which makes sure that its child elements are generated as many times as there are distinct vehicle regions defined in the reference table. -
The query in
popupContent
references the query inpopupGenerator
, and filters out the returned rows whosewhere_in_vehicle
field does not match the header of the popup. This makes sure that each popup panel only lists the relevant parts. -
The vehicle region values returned by the query in
popupGenerator
are referenced both inside the popup panel (inpopupLabel
), and outside it (inpopupHeader
). This makes sure that users will see the header for the Yes/No controls both when they open the form, and when they enter popup panels. -
Modify and save the file. To test whether the data binding works, you could add a new row to the input data spreadsheet with
Undercarriage
in thewhere_in_vehicle
before you publish the new form.Make the test changes, and publish (referencing
reftab.xlsx
when you publish via the mebt). A fourth popup panel should be generated in the form. -
Your form is now ready for any change in the
parts
reference table. Nice going.
Merge the user-generated dynamic list control and the reference data-generated dynamic list controls
The code looks much more compact and is more flexible, but you still have a separate
reference table to populate the drop-down choices in the
miscellaneousControl
panel.
What's even worse is that the same choices appear in every popup panel, because all
of them reference the same optionalParts
reference table. This is
definitely not good.
You don't want to set up a separate reference table for each of the vehicle regions.
Using the same logic as before, you could merge optionalParts
with
parts
, and add a column to filter the rows into the Yes/No
questions or the choices of the Add new part drop-down.
-
Download the input data spreadsheet that has new some rows for each of the vehicle regions, and indicates with 1 or 0 in the
required
column if a given row generates a Yes/No question or a choice in the optional car parts drop-down. -
Modify the form to make the query statements filter the data by the
required
column:... <Control type="panel" name="popupGenerator" navigation="inline" generator="SELECT DISTINCT where_in_vehicle FROM Reference_parts"> <Control type="label" name="popup" reference="SELECT @1" ref_arg="PARENT.col0"/> <Control type="panel" name="wrapper" navigation="popup"> <Control type="label" name="popupLabel" reference="SELECT @1" ref_arg="../../popup"/> <Control type="panel" name="popupContent" navigation="inline"
generator="SELECT part_name FROM Reference_parts WHERE required='1' AND where_in_vehicle=@1"
ref_arg="../../popup"> <Control type="label" name="SegmentedLabel" reference="SELECT @1" ref_arg="PARENT.col0"/> <Control type="combobox" name="Segmented" choice="button" 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="submitter" navigation="inline" generator="SELECT '' WHERE @1 ='No'" ref_arg="../Segmented"> <Control type="textbox" name="submitterDescription" label="Description"/> <Control type="photo" name="submitterPhoto" allowmultishoot="true" proposedresolution="1024x768" proposedquality="91"/> </Control> </Control> <Control type="panel" name="miscellaneousControl" navigation="inline" layout="standard" count="0"> <Control type="combobox" choice="dropdown" name="miscellaneousDropdown" text="Car part"reference="SELECT part_name FROM Reference_parts WHERE required='0' AND where_in_vehicle=@1" ref_arg="../../../../popup"/>
<Control type="textbox" name="miscellaneousDescription" label="Description"/> <Control type="photo" name="miscellaneousPhoto" allowmultishoot="true" proposedresolution="1024x768" proposedquality="91"/> </Control> <Control type="button" name="miscellaneousAddButton" text="Add part"> <Actions> <Action type="generator" target="../miscellaneousControl" removebuttontext="Remove"/> </Actions> </Control> </Control> </Control> ... -
miscellaneousDropdown
now referencesparts
instead ofoptionalParts
.The
generator
ofpopupContent
and thereference
ofmiscellaneousDropdown
both have a second conditional expression that references therequired
column inparts
. -
Take note of the
ref_arg
attribute of themiscellaneousDropdown
and thepopupLabel
elements: it has an 'unnecessary'../
in the path.This is in fact required, because these queries are inside a dynamic list control, but relatively address a control that is inside another dynamic list control. The extra
../
is needed to 'escape' the generated child element. -
Save and publish the file as usual.
Take a look at your creation: on your device, synchronize, if necessary, and check the choices for the Car part drop-down in the popup panels:
-
Fantastic job. You might possibly never have to touch this form again, and yet all its controls can be updated as often as necessary.
All it takes is changing the data in the
parts
reference table.The bulk of the final Vehicle Inspection form is now ready. Go on and finish it up in the next sections.