Bindings are where you specify data types, constraints, branching, and any other form logic. An example is shown below.
<bind nodeset="/data/branch" type="select1"/> <bind nodeset="/data/mystring" type="string" relevant="selected(/widgets/branch, 'n')"/>
Keyword
| keyword | usage |
|---|---|
| nodeset | Specify which instance element this binding is referring to |
| relevant | relevancy is how to branch through a form. If a node is not relevant, it doesn't appear to the user |
| readonly | If a node is read only, the user will only be able to view that prompt, not enter data |
| required | A required field will not allow a blank or empty answer before proceeding |
| constraint | constraint allows you to specify acceptable answers for the specified prompt |
| type | data type |
| calculate | allows you to run a calculation using this and/or other answers |
| jr:constraingMsg | The message that will be displayed if the specified constraint is violated |
| jr:preload | javarosa provides preloaders that can automatically give you information about the form. these are discussed below |
| jr:preloadParams | parameters used by jr:preload |
| jr:count | references a number used to determine number of repeats. Only used in the <body> |
Operators
| operator | usage | example | notes |
|---|---|---|---|
| this current prompt's answer | . | constraint=". < 10.51" | if current answer is less than 10.51 |
| not | not(expression) | relevant="not(selected(/widgets/select1, 'c'))" | as long as 'c' is not selected in /widgets/select1 |
| and | and | constraint="selected(., 'c') and selected(., 'd')" | both 'c' and 'd' need to be selected in the current prompt |
| or | or | constraint="selected(., 'c') or selected(., 'd')" | either 'c' or 'd' needs to be selected in the current prompt |
| greater than | > | constraint=". > 10.51" | greater than 10.51, can also be combined with equals: >= |
| less than | < | constraint=". < 10.51" | less than 10.51, can also be combined with equals: <= |
| selected | selected(xpath/to/node, value) | relevant="selected(/widgets/branch, 'n')" | checks if answer in selected prompt is selected |
| true | true() | required="true()" | |
| false | false() | required="false()" | |
| regular expression | regex(expression) | constraint="regex(., '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}')" | this particular regex checks for a valid email address |
| equal to | = | constraint=". = number('10')" | current answer must be equal to 10 |
| convert to boolean | boolean (xpath/to/node or value) | relevant="boolean(/widgets/branch)" | conversion varies depending on data type of x |
| convert to number | number (xpath/to/node or value) | constraint=". < number(/data/age)" | conversion varies depending on data type of x |
| convert to string | string (xpath/to/node or value) | "string (/data/age)" | conversion varies depending on data type of x |
| convert to date | date (xpath/to/node or value) | constraint=". >= date('2011-11-12')" | conversion varies depending on data type of x. format is yyyy-mm-dd |
| boolean from string | boolean-from-string(xpath/to/node) | boolean (xpath/to/node) | returns true if x is "true" or "1", false otherwise. note that this is different behavior than boolean(x) |
| conditional | if(condition, a, b) | calculate="if(selected(/data/q1, 'yes') and selected(/data/q2, 'yes'), 'yes', 'no')" | if true return a, else return b |
| count selected | count-selected(xpath/to/value) | count-selected(multi-select) | return the number of selected answers |
| count(nodeset) | count(nodeset) | count(nodeset) | count the number of nodes in nodeset |
| sum(nodeset) | sum(nodeset) | sum(nodeset) | return the sum of the values of the nodes in nodeset |
| today() | today() | today() | return today's date |
| now() | now() | now() | return a timestamp for this instant |
| at least X of, at most X of | checklist(min, max, v1, v2, v3, ..., vn) | checklist(min, max, v1, v2, v3, ..., vn) | v1 through vn are a set of n yes/no answers. return true if the count of 'yes' is between min and max, inclusive. min or max may each be -1 to indicate 'not applicable'. |
Below are common examples of bindings that may be useful.
Simple String
<bind nodeset="/widgets/mystring" type="string"/>
Required Integer
<bind nodeset="/widgets/int" type="int" required="true()"/>
Branching: Show this prompt if the answer to /widget/int is less than 8
<bind nodeset="/widgets/int2" type="int" relevant="/widgets/int < 8"/>
Branching: Show this prompt if the answer to /widget/branch (a select1) is "n"
<!-- the value 'n' is the <value>, not the <label> in the <item> in the select or select1 --> <bind nodeset="/widgets/language" type="string" relevant="selected(/widgets/branch, 'n')"/>
Constraints: Answer must be after date set in /widgets/date1
<bind nodeset="/widgets/date2" type="date"
constraint=". >= /widgets/date"
jr:constraintMsg="date must be after /widgets/date"/>
Constraints: Answer must be after today
<bind nodeset="/widgets/date" type="date"
constraint=". >= today()"
jr:constraintMsg="only future dates allowed"/>
Constraints: Answer must between 1 and 10 characters long (inclusive)
<bind nodeset="/widgets/string"
type="string" constraint="(regex(., "^.{1,10}$"))"/>
Constraints: "c" and "d" cannot be selected at the same time
<bind nodeset="/widgets/select" type="select" constraint="not(selected(., 'c') and selected(., 'd'))" jr:constraintMsg="option c and d cannot be selected together"/>
Constraints: Multi-lingual constraint message
<!-- use itext to define various language text -->
<bind nodeset="/data/prompt" type="int" constraint=". lt;= 10"
jr:constraintMsg="jr:itext('/data/prompt:error_message')"/>
Regular Expression: Answer must be an email address like bob@aol.com
<bind nodeset="/widgets/regex" type="string" required="true()"
constraint="regex(., '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}')"
jr:constraintMsg="this isn't a valid email address"/>
Repeat a group N times, where N is an answer to another prompt in the form
<!-- rare cases where the binding is defined in the body -->
<group>
<label>repeat node a</label>
<repeat nodeset="/data/repeat_node_a" jr:count="/data/repeat_count_a"
jr:noAddRemove="true()">
<input ref="/data/repeat_node_a/repeat_string_a">
<label>this should repeat <output value="/data/repeat_count_a"/>
times and finish</label>
</input>
</repeat>
</group>
You can add the binds below to your form for hidden or pre-loaded fields you want automatically filled in. You'll need a variable for them in the instance and a binding, but they have no body element and the user will never see or interact with them.
Form start time
<!-- stored the first time the form is loaded --> <bind nodeset="/widgets/start_time" type="dateTime" jr:preload="timestamp" jr:preloadParams="start"/>
Form end time
<!-- updated every time the form is saved --> <bind nodeset="/widgets/end_time" type="dateTime" jr:preload="timestamp" jr:preloadParams="end"/>
Current date
<bind nodeset="/widgets/date_today" type="date" jr:preload="date" jr:preloadParams="today"/>
Device ID (IMEI)
<bind nodeset="/widgets/deviceid" type="string" jr:preload="property" jr:preloadParams="deviceid"/>
Subscriber ID (IMSI)
<bind nodeset="/widgets/my_subscriberid" type="string" jr:preload="property" jr:preloadParams="subscriberid"/>
SIM serial number
<bind nodeset="/widgets/my_simid" type="string" jr:preload="property" jr:preloadParams="simserial"/>
Device phone number
<bind nodeset="/widgets/my_phonenumber" type="string" jr:preload="property" jr:preloadParams="phonenumber"/>
