A typical truck has much more parts than you can list in a form that lives on a mobile device screen. Any of the parts can have problems, and you want to let the Rocky Jupiter truck drivers report all of the problems they find - even if you don't list the problematic part in the form.
The solution is to let the driver add faulty parts to the list. You'll accomplish this by offering the user a drop-down menu to select an optional car part that she can then append to the checklist.
Because you don't want to overcomplicate your code or have to manage a second
reference table for the optional parts, you will slap its data into the
parts.xlsx
input data spreadsheet and include a helper variable
and multiple conditional statements to sort it out without additional form
controls.
Add a drop-down menu and an 'Add selected part' button
In the previous section, you've seen how to query reference table columns via data-binding and avoid having to hard-code data into your form. This makes your new task of adding Vehicle Inspection a lot more optional car parts for the user to choose from relatively painless.
A
dropdown
control is an economical way of presenting selection
options, and linking its choices
to a single-column reference table
seems straightforward enough. All you really need is a reference table declaration
file (get optionalparts.refem
here)
and an input data spreadsheet that conforms to it (here's reftab.xlsx
with
an optionalparts
worksheet).
<form id='vehicleInspection'...> ... <repeater id="popupGenerator"...> <popup...> <repeater id='partsList'...> ... </repeater><dropdown id='newparts' label='Pick a part to add' choices='{SELECT p.part_name FROM optionalparts p}' keyMap='{part_name}' textMap='{part_name}'/>
</popup> </repeater> </form>
When you save and publish the new and improved form, you'll be gladdened and saddened.
The dropdown
is there all right, but there's a number of
problems:
Once again, you've got two reference tables instead of one to manage.
All of your popup
s have the exact same additional
options.
There's no way for the user to actually add the selected car part to the checklist.
You'll be tackling all of these issues in this section.
Combine reference tables by adding a new column
optionalparts
shares the name of its column with one in
parts
, so lumping the two tables should be straightforward.
If you add a third column to parts
that determines whether a
given row is required or optional should do the trick. Why not make the column
accept integers just to spice things up a
bit?
This
change also takes care of the second problem cited above: all records have an
associated vehicle region, so you can make sure that each popup
has a distinct set of selectable options.
Here's the input spreadsheet to download.
As for the form, both the dropdown
control and the inner
repeater
need to be modified to accommodate the
change.
<form id='vehicleInspection'...> ... <repeater id="popupGenerator"...> <popup...> <repeater id='partsList' record='part' recordset='{SELECT p.part_name,p.required
, p.vehicle_region FROM parts p WHERE p.vehicle_region=generator.vehicle_regionAND p.required=1
}'> ... </repeater> <dropdown id='newparts' label='Pick a part to add' choices='{SELECT p.part_name,p.required
, p.vehicle_regionFROM parts p
WHERE p.vehicle_region=generator.vehicle_regionAND p.required=0
}' keyMap='{part_name}' textMap='{part_name}'/> </popup> </repeater> </form>
Make
sure that there is no reference to optionalparts
remaining in
your form, and query the required
column in both affected
controls.
Publish the form along with the reference table declaration and the input data spreadsheet, and see how you've killed two birds with one stone.
Now to that nagging matter of letting the user actually add the selected
car part to the list.
Having trouble letting users grow and shrink your in-form tables? Mobilengine
form language to the rescue! Meet the addbutton
(and evil twin removebutton
a bit later). It renders in the form as a plus sign in a green circle
with a bit of customizable text, and when tapped or clicked, adds a specific row
to a specific tabular control (read table
or
repeater
).
Ingredients for an addbutton
: a target table (specified in the
table
property) and one or more records
(records
property) that conforms to the target's
recordset
; your users will do the rest. In your case, the
target table should be the repeater
that generates the
segmentedbutton
s, and the record is based on the active
selection in the
dropdown
.
<form id='vehicleInspection'...> ... <repeater id="popupGenerator"...> <popup...> <repeater id='partsList'...> ... </repeater> <dropdown id='newparts'.../><addbutton table='partsList' records='{SELECT newparts.selectedValue.part_name, newparts.selectedValue.required, newparts.selectedValue.vehicle_region WHERE newparts.selectedKey IS NOT NULL}' text='Add selected part'/>
</popup> </repeater> </form>
Look
up the details on a dropdown
's selectedValue
if you feel a bit
confused.
Note the WHERE
clause at the end of the
records
query above, which makes sure that no empty rows
are added to the repeater
.
Save, publish the new form as usual, and admire your handiwork on your device.
Have you tried selecting a car part and then repeatedly tapping the
addbutton
? Your users are bound to.
You somehow need to guard against the addbutton
adding
the same record to the target table more than once. It would make the most sense
to not even have the user be able to select an option that is already present in
the repeater
. The part of the form that you'll need to modify
is therefore the dropdown
.
It's a bit of a head-scratcher, since your code has no idea about what the form
will render inside the repeater
: it all comes from the
reference table via data-binding. All you have is a template for
segmentedbutton
s, and it's all very dynamic - how can you
filter the options in the dropdown
with a dynamic list?
To be able to pull this off, you'll need the read-only rows
property (as seen in the data-binding section) unique to controls with a recordset
property. rows
is the list of all the rows that are rendered
inside the table
or repeater
. You can access
the controls rendered in each row in a subquery that references
rows
.
Once you know about it, nipping potential duplicates in the bud doesn't look so
intimidating. All you need to do is filter what the dropdown
's
query returns to exclude the car parts that match the
segmentedbutton
label
s inside the
repeater
.
<form id='vehicleInspection'...> ... <repeater id="popupGenerator"...> <popup...> <repeater id='partsList'...> <segmentedbutton id='yesNo'...> ... </segmentedbutton> ... </repeater> <dropdown id='newparts' label='Pick a part to add' choices='{SELECT p.part_name, p.required, p.vehicle_region FROM parts p WHERE p.vehicle_region=generator.vehicle_region AND p.required=0AND p.part_name NOT IN (SELECT k.yesNo.label FROM partsList.rows k)
}' keyMap='{part_name}' textMap='{part_name}'/> <addbutton table='partsList' records='{SELECT newparts.selectedValue.part_name, newparts.selectedValue.required, newparts.selectedValue.vehicle_region WHERE newparts.selectedKey IS NOT NULL}' text='Add selected part'/> </popup> </repeater> </form>
Save,
publish, and open on your device. You shouldn't be able to add the same car part
to the list of segmentedbutton
s twice. In fact, as the
screenshot below illustrates, if you've added all the available options for a
particular vehicle region, you can run out of options in the
dropdown
.
Groovy, but the UX designer would still be unhappy. The same
segmentedbutton
that the required options get won't work
for the the car parts added with the addbutton
, because the
user won't add them if they were operational. The user is expected to select and
add an optional car part only if they find an issue with it. Your list of
segmentedbutton
s must reflect this.
Use multiple conditions to streamline control flow
Make all of the optional car parts in the repeater
'malfunctioning' by default - frankly, that's why the user put them there. Don't
give optional parts a segmentedbutton
at all - just assume that
they were meant to be marked Malfunctioning, and give the
user a way to take them off the list if they change their mind. Put a removebutton
next to them, and you're set.
A removebutton
is a red no-entry sign with some custom text - if the
user taps it, its parent row is removed from the tabular parent control
(table
or repeater
).
Fine, you say, replace segmentedbutton
s with
removebutton
s for optional car parts, but how will make the
form decide whether to display a segmentedbutton
or a
removebutton
? Just as 'generate' says repeater, so 'decision' says
if
. Two of them, in fact: one for the required parts, and one
for those added as
optional.
<form id='vehicleInspection'...> ... <repeater id="popupGenerator"...> <popup...> <repeater id='partsList' record='part' recordset='{SELECT p.part_name, p.required, p.vehicle_region FROM parts p WHERE p.vehicle_region=generator.vehicle_region AND p.required=1}'><if cond="{part.required = 1}">
<segmentedbutton id="yesNo"...> ... </segmentedbutton></if>
<if cond="{part.required = 0}"> <removebutton text="Mark as operational" label="{part.part_name}"/> </if>
<if cond='{part.required = 0 OR yesNo.selectedKey == "Malfunctioning"}'>
... </if> </repeater> <dropdown.../> <addbutton.../> </popup> </repeater> </form>
Note
that the thirdif
(line 24) also needs some retro-fitting: optional parts are
assumed to be malfunctioning, so the textbox
and
photo
control needs to be added to them by default.
Save the form, publish, and open on your device to see those lovely
removebutton
s.
Figure 88. Optional car parts are now displayed with a removebutton
control and the additional reporting controls
As you'll notice, you've arrived at the final look of the form. Congratulations.
You're becoming a Mobilengine form-building threat. It's getting harder to find stuff to show you that you don't already know. Move on to the next section to see if there's anything new for you there.