To instruct docassemble to store user input that it receives in
response to a question, you need to include in your question
a
variable name to hold the information. You also
need to indicate what type of variable it is (e.g., text, a date, a
number), and how you want to ask for the input (e.g., with a label).
This section explains the many ways that variables can be populated
using question
s.
A note about variable names
Variable names are Python identifiers, which means they can be any
sequence of uppercase or lowercase letters, digits, and underscores,
except the first character cannot be a digit. No spaces are allowed
and no punctuation is allowed except for the underscore, _
.
The following are valid variable names:
fried_fish1
NyanCat
nyancat
(variables are case-sensitive, so this is not the same as the above)__f645456DG_greij_43
(but why would you use something so ugly?)USER_PHONE_NUMBER
(ok, but why are you yelling?)
The following are not valid variable names, and if you try to use such variable names you will may get an error or unexpected results:
8th_plaintiff
(you can’t begin a variable name with a number; Python will say “invalid syntax”)Nyan-Cat
(this is arithmetic:Nyan
minusCat
)fried.fish1
(this is valid code, but Python will think you are referring to the attributefish1
of the objectfried
)user's_phone_number
(apostrophes are not allowed; Python recognizes them as single quotes)favorite animal
(spaces are not allowed)beneficiary#1
(punctuation marks other than_
are not allowed)applicant_résumé
(only plain alphabet characters can be used)user.__ssn
(attributes beginning with__
are not allowed)for
orlicense.for
(attributes cannot share names with built-in Python syntax. Names likefor
,while
, orin
will result in errors if used as variable names or attribute names. See reserved variable names for a list of names that cannot be used.
If you find yourself using variable names like automobile_one
and
automobile_two
, you should learn about groups and
generalizing. It would make more sense to work with
variables automobile[0]
and automobile[1]
, or automobile[i]
.
If you find yourself using variable names like employment_income
,
self_employment_income
, and retirement_income
, you should learn
about the DADict
(a type of group). It would make more sense to
work with variables like income['employment']
,
income['self-employment']
, and income['retirement']
. Then you
could generalize the questions you ask.
And if you find yourself using variable names like
defendant_spouse_ssn
and defendant_spouse_date_of_birth
, you
should learn about objects. It would make more sense to work with
variables like defendant.spouse.ssn
and
defendant.spouse.birthdate
. There are many advantages of working
with objects, such as being able to write defendant.age_in_years()
and defendant.spouse.age_in_years()
to calculate the ages of people
based on their birthdates.
See reserved variable names for a list of variable names that you cannot use because they conflict with built-in names that Python and docassemble use.
Multiple choice questions (one variable only)
Yes or no questions
yesno
and noyes
yesno
causes a question to set a boolean (true/false)
variable when answered.
In the example above, the web app will present “Yes” and “No” buttons
and will set over_eighteen
to True
if “Yes” is pressed, and
False
if “No” is pressed.
noyes
is just like yesno
, except that “Yes” means
False
and “No” means True
.
Note that yes/no fields can also be gathered on a screen along with
other fields; to make screens like that, use
fields
below.
yesnomaybe
or noyesmaybe
These are just like yesno
and noyes
, except that they offer a
third choice, “I don’t know.” If the user selects “I don’t know,” the
variable is set to None
, which is a special Python constant that
represents the absence of a value.
Note that both False
and None
are considered to be “false” values
in Python. So if you write:
then the phrase “You are a dummy!” will be shown both if the value is
False
and also if the value is None
. If you need to test
specifically for the “I don’t know” answer, use is None
.
Multiple choice buttons
A question
block with buttons
will set the variable
identified in field
to a particular value depending on which of the
buttons the user presses.
buttons
must always refer to a list, so that docassemble knows
the order of the buttons.
If an item under buttons
is a YAML key-value pair (written in the
form of - key: value
), then the key will be the button label that the
user sees, and the value will be what the variable identified in field
will be set to if the user presses that button.
An item under buttons
can also be plain text; in that case
docassemble uses this text for both the label and the variable
value.
In other words, this:
is equivalent to this:
You can also provide the label and the corresponding value by using a
dictionary containing keys label
and value
.
You can customize the appearance of buttons by specifying a css
class
and/or a color
in the dictionary.
The color
should refer to one of the Bootstrap colors (primary
,
secondary
, success
, danger
, warning
, info
, light
, link
,
or dark
).
If you want one of the choices to be shown or not shown conditionally,
you can use show if
to specify a Python expression. If the
expression evaluates to true, then the choice will be included, and if
it evaluates to false, it will be excluded.
Using code to generate the choices
A powerful feature of buttons
(which also works with
choices
, dropdown
,
and combobox
) is the ability to use Python
code to generate button choices. If an item under buttons
is a
key-value pair in which the key is the word code
, then
docassemble executes the value as Python code, which is expected
to return a list. This code is executed at the time the question is
asked, and the code can include variables from the interview.
docassemble will process the resulting list and create additional
buttons for each item.
Note that the Python code needs to return a list of key-value pairs (Python dictionaries) where the key is what the variable should be set to and the value is the button label. This is different from the YAML syntax.
This is equivalent to:
You can mix choices that are specified manually with choices that are specified with code:
Instead of using key-value pairs to represent what the variable is set
to and the label, you can use value
and label
as
keys in the dictionary. You can also use the dictionary keys css
class
and color
to modify the appearance of the buttons, and show
if
to conditionally include the button.
The color
should refer to one of the Bootstrap colors (primary
,
secondary
, success
, danger
, warning
, info
, light
, link
,
or dark
).
As explained below, you can also use code to decorate the buttons with images.
If you need the variable to have a data type other than text, you need
to specify a datatype
.
The possible datatype
values include boolean
(True
or False
),
threestate
(True
, False
, or None
), and other
data types.
True/False buttons
You can use buttons
as an alternative to yesno
where you want
different text in the labels.
In order for the variable to be set to the special Python values
True
and False
, you need to make sure that the only values you
list are True
and False
, and nothing else, just like in the
example above. If you include a different value, your variable will
be set to 'True'
or 'False'
, which could cause problems.
Multiple choice list
To provide a multiple choice question with “radio buttons” and a
“Continue” button, use field
with a choices
list:
You can specify a default value using default
:
Another way to set a default is by adding default: True
to the
choice that you want to be the default.
You can also provide help text for a radio button using help
:
You can customize the appearance of a particular item by specifying
color
and css class
:
The color
should refer to one of the Bootstrap colors (primary
,
secondary
, success
, danger
, warning
, info
, light
, link
,
or dark
).
If you want a choice to be included conditionally, you can use show
if
to specify a Python expression that indicates when the choice
should be included or not.
These customizations can also be specified when building a list of choices using code:
Multiple choice dropdown
To provide a multiple choice question with a dropdown selector, use
field
with a dropdown
list:
Multiple choice combobox
To provide a multiple choice question with a “combobox” selector, use
field
with a combobox
list:
The “combobox” selector allows users to choose a selection from a list or enter a value of their own.
Adding images to buttons and list items
To add a decorative icon to a buttons
choice, use a key/value pair
and add image
as an additional key.
This works with choices
as well:
It is not possible to decorate dropdown
or combobox
choices with
images.
In these examples, calendar
and map
are the names of decorations
that are defined in an images
or image sets
block.
If you create the list of choices with
code
, you can specify an image by
including an additional key/value pair within an item, where the key
is image
.
There is an additional feature available when you assemble buttons
with code
: you can use DAFile
or
DAFileList
objects to indicate the image. This example uses an
uploaded image file as the source of the image for one of the buttons:
Embedding question
and code
blocks within multiple choice questions
Multiple choice questions can embed question
blocks and code
blocks. These questions are just like ordinary questions, except they
can only be asked by way of the questions in which they are embedded.
You embed a question by providing a YAML key-value list (a
dictionary) (as opposed to text) as the value of a label in a
buttons
, choices
, or dropdown
list.
While embedding question
blocks can be useful sometimes, it is
generally not a good idea to structure interviews with a lot of
embedded questions. You will have more flexibility if your questions
stand on their own. Embedded blocks cannot use the generic object
modifier or index variables.
It is also possible for multiple-choice questions to embed code
blocks that execute Python code. (If you do not know what code
blocks are yet, read the section on code blocks first.) This can be
useful when you want to set the values of multiple variables with one
button.
The question above tells docassemble that if the interview logic
calls for either car_model
or car_make
, the question should be
tried. When the user clicks on one of the buttons, the code will be
executed and the variables will be set.
To undo a user’s choice on a question
that embeds blocks, tag the
question
with an id
and call the forget_result_of()
function with the ID.
Questions with only a “continue” button
A question
with merely a continue button field
will offer the
user a “Continue” button. When the user presses “Continue,” the
variable indicated by continue button field
will be set to True
.
If you are using fields
and you want the “Continue” button to set
a variable to True
the way that this question
type does, you can
add the continue button field
specifier.
Questions that collect one or more fields on a screen
So far, we have discussed questions that set a single multiple-choice
variable and the use of continue button field
to set a
single variable to True
. These are helpful when you are collecting
True
or False
values or multiple choice values. However,
docassemble’s primary tool for collecting information in is the
fields
specifier. fields
allows you to collect many different
types of information and to collect more than one piece
of information on a screen.
The fields
specifier must refer to a YAML list of one or more
“fields”. Each list item must consist of one or more key/value pairs.
One of these keys (typically) is the label the user sees,
where the value associated with the key is the name of the variable
that will store the user-provided information for that field. The
other key/value pairs in the item (if any) allow you to modify how the
field is displayed to the user.
These field modifiers are distinguished from label/variable pairs based on the key; if the key uses one of the names listed below, it will be treated as a field modifier; if it is anything else, it will be treated as a label.
The next section describes the different types of variables you can
gather with fields
and the different types of user interfaces you
can use.
Data types and input types
Within a fields
question, there are many possible datatype
values that you can use. These affect what the user sees and how the
input is stored in a variable.
The possible values of datatype
are:
user
camera
environment
camcorder
checkboxes
multiselect
currency
date
datetime
email
file
files
integer
microphone
ml
mlarea
noyes
noyesmaybe
noyesradio
noyeswide
number
object
object_checkboxes
object_multiselect
password
range
text
(the default)time
yesno
yesnomaybe
yesnoradio
yesnowide
In most cases, datatype
controls both the user interface and the
format in which the data is stored. But for certain multiple choice
questions, you can use datatype
to indicate how you want the data
stored, and use input type
to indicate the type of user interface
to use. The possible values of input type
are:
The following subsections describe the available datatype
s and
input type
s that you can assign to a field within fields
.
Plain text
A datatype: text
provides a single-line text
input box. This is the default datatype
, so you never need to
specify it unless you want to.
input type: area
provides a multi-line text area.
You can change the number of rows in the text area using the rows
specifier:
The default number of rows is four.
Passwords
datatype: password
provides an input box
suitable for passwords.
Dates
datatype: date
provides a date entry input box. The style of the
input box depends on the browser.
Validation is applied to ensure that the date can be parsed by
dateutil.parser.parse
.
The variable resulting from datatype: date
is a special Python
object of the class DADateTime
, which is a subclass of the
standard Python class datetime.datetime
. So if the name of the
date variable is date_of_filing
, then you can do things like:
Note that the field on the screen only asks for a date, but
DADateTime
represents both a date and a time. The time portion of
the DADateTime
object will be set to midnight of the date. If you
want a DADateTime
with a time other than midnight, you can use the
.replace_time()
or .replace()
methods of DADateTime
to
generate a new object with the same date but a different time.
For more information about working with date variables, see
the documentation for the date functions. These functions are
generally very flexible about formats, so you can pass a string like
'12/25/2018'
or a date object, and the function will produce the
correct result either way.
In particular, if you want to format a date variable for inclusion in
a document or a question, you will probably want to use the
.format_date()
method or the format_date()
function.
To set a default value, you can set default
to any value that can
be understood as a date.
Likewise, to set limits, you can set min
and/or max
to a string
that can be recognized as a date.
Times
datatype: time
provides an input box for times. The style of the
input box depends on the browser.
Validation is applied to ensure that the time can be parsed by
dateutil.parser.parse
.
The resulting variable will be an object of type datetime.time
.
To indicate a default time, write a default
value in the format
13:43:23
. If you have a datetime.time
variable called
meeting_start
and you want the value of meeting_start
to be the
default time for a field, you can set the default
value to ${
meeting_start }
. This has the same effect as str(meeting_start)
or
meeting_start.strftime('%H:%M:%S')
.
If you want to format a time variable for inclusion in a document or a
question, see the .strftime()
method or the format_time()
function.
If you want to gather both a date and a time from a user, and combine
the values together into a single DADateTime
object, you can do so
with the .replace_time()
method. For example:
If you want to format a date and time for inclusion in a document or a
question, see the .format_datetime()
method or the
format_datetime()
function.
Combined dates and times
datatype: datetime
provides an input box for dates and times
together in one field. The style of the input box depends on the
browser. Note: not all browsers have a “widget” for combined date and
times, and users might be confused if they are presented with a plain
text box. For this reason, use of datatype: datetime
is not
recommended until browser support for the
datetime-local
becomes more widespread.
Validation is applied to ensure that the time can be parsed by
dateutil.parser.parse
.
The resulting variable will be an object of type DADateTime
. The
object can be formatted using the .format_datetime()
method or the
format_datetime()
function.
E-mail addresses
datatype: email
provides an e-mail address input box.
Numbers
datatype: integer
indicates that the input
should be a valid whole number.
datatype: number
indicates that the input
should be a valid numeric value.
You can use the optional field modifier step
to limit the number to a
certain number of decimal places:
Currency
datatype: currency
indicates that the input should be a valid
numeric value. In addition, the input box shows a currency symbol
based the locale
defined in the configuration.
The variable will be set to a number, just as if datatype: number
was used. For information about how to display currency values, see
the currency()
function.
If the locale convention places the currency symbol after the number, the currency symbol will be placed before the field; otherwise it will be placed after the field.
If the currency symbol defined by the locale is not the currency you
want to use, you can include an initial
block that calls
set_locale()
with the currency_symbol
keyword parameter set to
the symbol you want to use. This will set a default value for
datatype: currency
fields and for the currency()
function.
Keep in mind that the variable stored by a datatype: currency
field
is just a number, so it is not aware of the currency denomination that
was presented to the user when the information was collected.
You can also override the currency symbol on a field-by-field basis by
setting the currency symbol
field modifier.
As this interview demonstrates, the currency()
function accepts an
optional keyword parameter symbol
that allows you to override the
symbol that is displayed.
Sliders
datatype: range
shows a slider that the user can use to select a
number within a given range. The range must be supplied by providing
min
and max
values. An option step
value can also be provided,
the default of which is 1.
You can also include an optional scale
, which you can set to
logarithmic
.
File uploads
Using the file
or files
datatypes within a fields
list, you can
allow users to upload one or more files.
datatype: file
indicates that the user can upload a single file.
The variable is set to a DAFileList
object containing the
necessary information about the uploaded file.
datatype: files
indicates that the user can upload one or more
files. The variable is set to a DAFileList
object containing the
necessary information about the uploaded files.
If you want to limit uploads to particular file
types, you can use accept
to specify Python code that returns a
custom accept attribute. The value of accept
is passed directly
into the accept attribute in the HTML and into the accept
method
of the jQuery Validation Plugin.
Note that the syntax is very specific: the double quotation marks are part of the string itself.
Since the accept
method of the jQuery Validation Plugin only
works with MIME types, you can only specify MIME types here, not file
extensions.
By default, docassemble styles the
upload using the Bootstrap File Input plugin. If you do not want the
Bootstrap File Input plugin to be used, you can set file css class
to None
.
If you set file css class
to None
, then the class
of the <input
type="file">
element will be form-control
, which is the standard
class that Bootstrap uses to style file input elements. You can set
file css class
to any other class of your choosing if you want to
use a different class
than form-control
. The file css class
modifier can use Mako templating.
If no file css class
is specified, the class
of the input element
will be dafile
, which causes the Bootstrap File Input plugin to
be activated.
Note that file css class
is different from css class
; the css
class
modifier simply adds additional classes to the class
attribute of the input element, whereas file css class
replaces the
default class, which is dafile
.
If your users upload digital photos
into your interviews, the uploads may take a long time. You can
configure an upload field so that images are reduced in size before
they are uploaded by modifying your field definition with a
maximum image size
. The image will be reduced in size so that is
no taller than or wider than the number of pixels designated by
maximum image size
.
In this example, images will be reduced in size to no more than 100 pixels in height or width:
Note that the image file type of the uploaded file may be changed to PNG during the conversion process. Different browsers behave differently.
If you have a lot of document upload fields, you can set a default
maximum image size
on an interview-wide basis with the
maximum image size
interview feature and on a site-wide basis with
the maximum image size
configuration directive. If you have a
default set up, but you want to override it for a particular field,
you can set the maximum image size
field modifier to None
.
If you are using maximum image size
,
you can also cause images to be converted to PNG, JPEG, or BMP
by the browser during the upload process by setting the image upload
type
to png
, jpeg
, or bmp
.
By default, any file that a user uploads
during a session will be deleted when that session is deleted. If you
want the file to continue to exist after the session is deleted, you
can set the field modifier persistent
to True
. The modifier also
accepts Python code; if the code evaluates to a true value, the file
will persist. This has the same effect as calling the
.set_attributes()
method on the file variable using the keyword
attribute persistent
.
By default, any file that a user uploads will
only be downloadable by the user or by an administrator. If you want
the file to be accessible to anyone, set the field modifier private
to False
. The modifier also accepts Python code; if the code
evaluates to a false value, the file will be available to anyone.
This has the same effect as calling the .set_attributes()
method
on the file variable using the keyword attribute persistent
.
If you set private: False
, then the file
is available to anyone, including non-logged in users. Even a bot
that guesses URLs could download the file. If you want to share with
particular users, you can indicate specific users using the allow
users
modifier.
If allow users
refers to a YAML list, the list is expected to be a
list of e-mail addresses of users or integers indicating the numeric
user IDs of users. If allow users
refers to text, the text is
treated as a single item.
Mako is not available; however, if allow users
refers to a YAML
dictionary, the single key of which is code
, you can specify users
with Python code. The code is expected to evalute to an e-mail
address, an integer user ID, an Individual
with the email
attribute set, or a list or DAList
of any of the above.
You can also use the .user_access()
method to control which users
have access to a file.
Instead of granting access to specific
other users, you can use the allow privileges
field modifier to
grant access to categories of users by referencing privileges by
name, such as user
, developer
, or advocate
. The
allow privileges
modifier works much like the allow users
modifier. If the allow privileges
modifier refers to a YAML list,
the list items are expected to be text items like user
or
developer
. If allow privileges
refers to a string, it is treated
as a single item. Mako is not allowed. If allow privileges
refers
to a YAML dictionary, the single key of which is code
, you can
define the privileges using Python code, which is expected to
evaluate to text (e.g., 'user'
) or a list of text strings (e.g.,
['user', 'developer']
). You can also use the
.privilege_access()
method to control which users have access to a
file.
There are a few other data types that result in file uploads:
datatype: camera
is just like file
, except
it limits the allowable file types to image files.
datatype: user
is just like camera
, except with
an HTML5 input type that suggests using the device’s front
(user-facing) camera.
datatype: environment
is just like
camera
, except with an HTML5 input type that suggests using the
device’s rear (environment-facing) camera.
datatype: camcorder
is just like file
,
except it limits the allowable file types to video files.
datatype: microphone
is just like file
,
except it limits the allowable file types to audio files.
For more information about uploading files, and for instructions on uploading signature images, see the Uploads subsection.
Yes/no fields
datatype: yesno
will show a checkbox with a label, aligned with
labeled fields. datatype: noyes
is like datatype: yesno
, except
with True and False inverted.
datatype: yesnowide
will show a checkbox with a label
that fills the full width of area. datatype: noyeswide
is like
datatype: yesnowide
, except with True and False inverted.
Sometimes, when you are using a series of
these checkboxes, you might want to have a “none of the above”
selection. To do this, add a field for the selection, and associate
it with a variable. (Your interview does not need to use the
variable.) Then modify the field with uncheck others: True
.
This will cause the field to act as a “none of the above” field for
all the other yes/no checkbox fields on the page. If you want the
field to only relate to specific other fields, use a list of the
variable names of those fields instead of True
.
Other times, when you are using a series of
these checkboxes, you might want to have an “all of the above”
selection. To do this, add a field for the selection, and associate
it with a variable. (Your interview does not need to use the
variable.) Then modify the field with check others: True
.
This will cause the field to act as an “all of the above” field for
all the other yes/no checkbox fields on the page. If you want the
field to only relate to specific other fields, use a list of the
variable names of those fields instead of True
.
datatype: yesnoradio
will show radio
buttons offering choices “Yes” and “No.”
datatype: noyesradio
is like
datatype: yesnoradio
, except with True and False inverted.
datatype: yesnomaybe
will show radio
buttons offering choices “Yes,” “No,” and “I don’t know.” The
resulting Python values are True
, False
, and None
.
datatype: noyesmaybe
is like
datatype: yesnomaybe
, except with True and False inverted.
When you provide help
text for a yesno
field, the help
will be available as a popup accessible from a button located to the
right of the field.
Checkboxes
datatype: checkboxes
will show the choices
list as
checkboxes. The variable will be a DADict
(a type of dictionary
specific to docassemble) with items set to True
or False
depending on whether the option was checked.
As you can see in this example, the keys of the resulting dictionary
are the names of fruit, the values that are checked are True
, and
the values that were not checked are False
.
In the example above, the keys of the dictionary are the same as the labels displayed to the user. If you want labels to be different from the keys, you can specify the choices in the following manner:
You can also express the checkboxes as a list of dictionaries where
each dictionary has the keys label
and value
.
The all_true()
, all_false()
, any_true()
, any_false()
,
true_values()
, and false_values()
methods of DADict
can be
used to analyze the values set by a checkboxes field. For example:
If you want to require the user to select a minimum or maximum number
of checkboxes, you can use the minlength
and/or maxlength
field
modifiers.
You can generate checkbox choices with code:
Default values for checkboxes
To set default values in a checkbox list, you have a few options.
If you want to select just one option, just indicate the name of the option:
If you want to select multiple options, indicate a YAML list:
You can also indicate your defaults in the form of a YAML dictionary:
You can also use Python code to generate the defaults:
Your Python code can also return a dictionary:
If you generate the checkbox options with code
, you can include
defaults directly within your code when you use a list of
dictionaries:
This also works if your code returns a list of lists:
Multiselect
datatype: multiselect
works much like datatype: checkboxes
, except
it uses the HTML <select>
element with the multiple
flag set.
On desktop browsers, multiple items can be selected by clicking items
while holding down the Ctrl or Command key and clicking each item.
Unlike datatype: checkboxes
, the datatype: multiselect
field does
not support the use of a “None of the above” option.
You can use the rows
specifier to indicate how many rows tall the
multiselect box should be:
Multiple-choice dropdown
If you provide a list of choices
or some
choice-generating code
for a field within a list of
fields
, the user will see a dropdown. The variable will be set to
the value of the selected choice.
You can also include input type: dropdown
:
The input type: dropdown
does not actually have any effect, since
dropdown
is the default input type
. (The other options for input
type
are radio
and combobox
.)
The code
option, which uses Python code to generate the
list of choices, is often used in combination with
exclude
, which excludes one or more items from the list
of choices.
Multiple-choice combobox
input type: combobox
shows a choices
list as a
combobox instead of as a dropdown select element (which is
the default).
The “combobox” selector allows users to choose a selection from a list or enter a value of their own.
Combobox that fetches choices from the server
input type: ajax
looks like a combobox
, but is really a dropdown
selector that retrieves its choices from the server using Ajax,
based on what the user types. It is useful when the number of possible
values in the dropdown is too large to send to the browser all at
once, and you want to allow the user to find the item they want to
select by typing the start of a word or phrase.
To use input type: ajax
, you also need to supply an action
specifier. The browser will use the JavaScript function
url_action_call()
to call the given action. The text that the user
types into the field will be passed to the action as the wordstart
argument. The action needs to return a JSON list of items.
The following example uses the words file (from the wamerican package) as a data source for the combobox options.
The code
block that carries out the action
should always begin
with set_save_status('ignore')
. If you leave this out, then a step
will be added to the interview each time the results are fetched. The
code
block should always end by calling json_response()
that
returns the relevant choice or choices.
The data that you pass to json_response()
can be in one of three
forms:
- A
list
of pieces of text; - A
dict
in which the keys are the underlying values (what the variable will be set to) and the values are labels (what the user sees and types); or - A
list
oflist
s, where the first item in each sub-list is the underlying value and the second item is the label.
If you use the second or third option, note that docassemble will
only store the underlying value in the variable, even though the user
typed the label. In order for your datatype: ajax
field to function
properly if the question
is revisited during a review process or the
use of the Back button, your action
needs to be able to accept as
input either the underlying value or the label. In the following
example, note the special return value if the wordstart
argument
matches a key in the dictionary.
If you press the Back button to return to the datatype: ajax
field,
the initial value of the field will be 'x234'
, 'y432'
, or
'h293'
. This value will be sent to the action
to be looked up, and
then the screen will show the label rather than the value.
In order to avoid sending too many requests to the system, the requests are throttled so that they happen no more than once every two seconds.
The list will not start showing results until the user types at least
four characters. If you want to use a different number of characters
as the minimum, set trigger at
. For example:
Radio buttons
input type: radio
shows a choices
list as a list of
radio buttons instead of as a dropdown select element (which is
the default). The variable will be set to the value of the
selected choice.
Multiple-choice with objects
datatype: object
is used when you would like to use a variable to
refer to an existing object. You need to include
choices
, which can be a list of objects.
If choices
refers to a variable that is a list of
things, the list will be unpacked and used as the list of items from
which the user can select. If choices
refers to a
string, that string is expected to be a Python expression that
returns a list of objects. If choices
refers to a YAML
list, then each item in the list is expected to be a Python
expression that returns either an object or a list of objects.
By using datatype: object
in combination with disable others
,
you can create questions that either set the attributes of an object
or set the object equal to another object.
In this example, if the gardener and the cook are the same person, the interview effectively does the following in Python:
Please note that datatype: object
cannot be used with
the generic object
modifier if the variable being set is
x
.
datatype: object_radio
is like datatype:
object
, except the user interface uses radio buttons rather than a
pull-down list.
For a fuller discussion on using multiple-choice object selectors, see the section on selecting objects, below.
datatype: object_checkboxes
is
similar to datatype: object
, except it results in an object of type
DAList
(or a subtype thereof) consisting of zero or more items
selected by the user. The choices specified in choices
(optionally modified by exclude
) will be presented to the user as
checkboxes. The .gathered
attribute of the variable will be set to
True
after the elements are set. See groups for more information.
You can use datatype: object_checkboxes
on variables that already
exist in your interview. You would need to do this if you wanted the
variable to be a subtype of DAList
. If you use a variable name
that already exists, note that the question
will only be used when
the .gathered
attribute is needed. To avoid questions asking for
.there_are_any
and .there_is_another
, set .auto_gather
to
False
. For example:
Note the placement of the objects
block that defines villain
as a
PartyList
. If this objects
block came before the question
that defines villain
, then the question
block would take
precedence over the objects
block and define villain
as a plain
DAList
. Since the objects
block is placed after the question
,
it supersedes the question
, and defines villain
as a PartyList
.
The question
will still be asked, however, because even if
villain
is defined, it is not yet gathered; the question
will be
asked when a definition of villain.gathered
is needed.
When you use an already-existing DAList
, you can set default values
of the checkboxes in the object_checkboxes
list. In this example,
we use the .append()
method to initialize the list of villains.
This example illustrates a second method of making sure that villain
gets defined as a PartyList
: marking the objects
block with
mandatory: True
. This causes each variable in the objects
list to
be defined as an object before the rest of the interview logic is
evaluated.
datatype: object_multiselect
is
similar to datatype: object_checkboxes
, except it uses the HTML
<select>
element with the multiple
flag set. On desktop browsers,
multiple items can be selected by clicking items while holding down
the Ctrl or Command key and clicking each item.
Machine learning
From the user’s perspective, datatype: ml
works just like datatype:
text
(which is the default if no datatype
is indicated), and
datatype: mlarea
works just like datatype: area
.
From the interview developer’s perspective, however, the variable that is set is not a piece of text, but an object representing a classification of the user’s input, based on a machine learning model that is “trained” to classify user input.
For more information about how to use machine learning variables, see the machine learning section.
Hidden field
input type: hidden
results in an invisible field that can only be
changed from its default value by JavaScript.
This can be useful if you want fields to be populated by the address autocomplete feature but you do not want the fields to be shown to the user.
If you think you need to use input type: hidden
, but you are not
using the address autocomplete feature and you have not written your
own JavaScript code to populate the field, then you most likely
should not use input type: hidden
, and should perhaps use a code
block instead. The input type: hidden
feature exists solely for
interacting with JavaScript and is not part of docassemble’s
logic system.
No browser-based input validation is performed on a field with input
type: hidden
. If you need input validation on a input type: hidden
field, use validation code
. An error message cannot be displayed
next to a hidden field.
Raw data
By default, inputs with the datatype
of text
(which is the
default) will be sanitized of any HTML. If you want to allow users to
include HTML, set the datatype
to raw
.
Custom data types
You can use custom data types by declaring a subclass of
CustomDataType
in a Python module with class attributes that
describe the data type. For example, here is an example that defines
a Social Security number (SSN) as a data type:
This will allow you to write:
The user will not be able to proceed without entering a valid SSN, and
if the user enters an SSN without hyphens, the input will be accepted,
but hyphens will be added to the variable user.ssn
.
The available class attributes are:
name
(required) - thedatatype
name. The only valid characters are alphanumeric characters, the hyphen, and the underscore.container_class
- a CSS class for the parent container. By default, this will be set toda-field-container-datatype-
followed by thename
.input_class
- a CSS class for the<input>
element. By default, this will be set toda
followed by thename
.input_type
- thetype
for the<input>
element. By default, this will be set totext
.javascript
- JavaScript code related to the data type. By default, this will beNone
. If notNone
, the attribute is treated as JavaScript code that is run when every interview on your server first loads. This is typically used to extend the capabilities of the jQuery Validation Plugin. In the example above, thejavascript
defines a new validation rule. For more information on how to extend the jQuery Validation Plugin, see the documentation for jQuery.validator.addMethod(). Thejavascript
code can be used to initialize any fields on the screen that have the custom datatype. For example, if yourcontainer_class
isda-ssn
, yourjavascript
could be:
jq_rule
- the name of the jQuery Validation Plugin rule to enable on the field, if any. This is typically used to refer to a rule you define in thejavascript
attribute. If you want to enable multiple jQuery Validation Plugin rules, you can set this to a list of rule names.jq_message
- the message to display to the user when thejq_rule
is not satisfied. The message will pass through theword()
function to support multiple languages, and it can be overridden withvalidation messages
. Instead of settingjq_message
to a string, you can set it to a dictionary in which the keys are jQuery Validation Plugin rule names and the values are error messages. For example, if you want a custom message to display when the user leaves the field blank, you can setjq_message
to something likejq_message = {'ssn': 'You need to enter a valid SSN.', 'required': 'We really need your SSN.'}
. If no message is provided for a rule, a generic message is used. Ifjq_rule
is a list of rules,jq_message
must be expressed in dictionary format.skip_if_empty
- the default isTrue
. This is rarely used, so you can probably ignore it. This is relevant when thedatatype
is used on a multiple-choice question and there are zero choices to present to the user. Ifskip_if_empty
isTrue
, then the variable is not set to any value. Ifskip_if_empty
isFalse
, then the variable will be set to the output of the.empty()
class method.is_object
- the default isFalse
. If you have atransform()
class method that returns something that cannot be defined withrepr()
, set this toTrue
.parameters
- the default is[]
. If you want to pass parameters from the YAML todata
attributes of the resulting<input>
, you can list the parameter names here. For example, ifparameters
is set to['kind']
, and you includekind: basic
as a field modifier in the YAML of the field, then the HTML of the<input>
element will containdata-kind="basic"
. You can extract these data values using JavaScript.code_parameters
- the default is[]
. This is just likeparameters
, except the values in the YAML are treated as a Python expression, and thedata
value is set to the output of this evaluation. Make sure that the Python expression evaluates to something sensible, like a string.mako_parameters
- the default is[]
. This is just likeparameters
, except the values in the YAML are treated as Mako, and thedata
value is set to the rendered text.
The available class methods are:
validate()
- thevalidate()
class method is used for server-side input validation. The raw value from the POST request is passed to this method as the first positional parameter. The second positional parameter is the name of the variable, as a string. The third positional parameter is a dictionary containing the parameters specified in the YAML (seeparameters
, etc., above). The method should returnTrue
if the value is valid. If the value is invalid, the method should raise aDAValidationError
exception with a message. The message given toDAValidationError
will pass through theword()
function before it is presented to the user, so you can use thewords
directive in the Configuration to support multiple languages. If the method returns a false value, the error message will be “You need to enter a valid value.” If avalidate()
class method is not provided, no input validation will be performed.transform
- thetransform()
class method is used to perform any necessary transformations on the data received from the browser. The raw value from the POST request is passed to this method as the first positional parameter. The second positional parameter is the name of the variable, as a string. The third positional parameter is a dictionary containing the parameters specified in the YAML (seeparameters
, etc., above). The method should return the transformed value. In the example above, thetransform()
class method ensures that Social Security numbers that are entered without hyphens (which are accepted) will contain hyphens when the variableuser.ssn
is actually defined. If atransform()
class method is not provided, the variable will be set to the raw value from the POST request (a string).default_for
- thedefault_for()
method is used when the user visits aquestion
when the variable is already defined. If the output of yourtransform
method is not suitable for placing into the field as the default value, you can define adefault_for()
class method that returns the text that should be inserted into the field. The first positional parameter for thedefault_for()
class method is the value of the variable. The second positional parameter is the name of the variable, as a string. The third positional parameter is a dictionary containing the parameters specified in the YAML (seeparameters
, etc., above). If yourtransform()
method returns a Python object, you can write adefault_for()
class method that takes the object as the first positional parameter and returns plain text (anstr
) that is a suitable default value to use for the HTML field. If you do not define adefault_for()
class method, an attempt will be made to obtain a default value by runningstr()
on the variable.empty
- this is rarely used, so you can probably ignore it. Theempty
class method is used whenskip_if_empty
isFalse
. Instead of not defining the variable, docassemble will set the value of the variable to the output of this method. If noempty()
method is provided, the valueNone
is used.
Here is an example that demonstrates the use of the transform()
,
validate()
, and default_for()
methods.
An example of using this data type would be:
The favorite_fruit
variable will become a Thing
object. The value
of the HTML <input>
element becomes the .name.text
attribute of
the object. In addition, the .seeds
attribute of the object is
defined using the custom parameter seeds
. The validation method
limits the number of words that can be used, based on the parameter
max words
, which can contain a Python expression.
Note that the default_for()
and transform()
objects are
complementary; the default_for()
converts a Thing
object to a
string, and transform()
converts a string into a Thing
object.
The default_for()
method needs to be flexible because the default
value of the field that is passed to the default_for()
method may be
plain text or a Thing
object, depending on the circumstances. The
value will be a string if a default
field modifier is used (which is
always treated as a Mako expression that returns a string), or if
there is a validation error, in which case the default value becomes
the string that was found invalid.
Python classes, when loaded into the Python web application, are
loaded globally across all threads of the server; they are not loaded
just for one interview or just for one session. Likewise, the name
attribute associated with a CustomDataType
class is also global on
the server. For this reason, you may wish to “namespace” your
CustomDataType
name
s, using a name like aag_ssn
instead of of
ssn
(e.g., if your company name is AAG). That way, if a number of
different packages are used together, it is less likely there will be
“name collision.”
By default, docassemble will load the JavaScript for any
CustomDataType
data types that are used in the interview
YAML. However, if fields are created by Python code, docassemble
cannot detect what CustomDataType
data types will be used. In this
circumstance, you can manually list them under custom datatypes to
load
specifier under features
, and the necessary JavaScript
will be loaded.
How custom data types work
When the server starts, it looks through all of the packages under the
docassemble
namespace and loads every .py
file that contains a
class definition (unless the line # do not pre-load
is present). It
is during this initial loading time that CustomDataType
class
definitions are processed and loaded. As a result, you do not need to
load the .py
file in your interview with modules
or imports
when you use a datatype
from a CustomDataType
class in your YAML.
When choosing a name
for your CustomDataType
, do not use a name
that someone else might use. Because CustomDataType
class
definitions are processed when the server starts, all CustomDataType
definitions are global to the server; they are not specific to a
particular interview, session, or user. If another module is
installed on the system (including in the Playground of some other
user) that defines a CustomDataType
with the same name
as that
which you are trying to define, one definition will overwrite the
other and you may get confusing results.
Options for items in fields
The following are the keys that have special meaning within a list
item under fields
.
datatype
datatype
affects how the data will be collected, validated and
stored. For a full explanation of how this is used, see
above.
input type
The input type
is similar to datatype
. It is used in situations
where the datatype
might be date
, number
, etc., but you want
the field to use a particular type of multiple-choice input element,
such as a list of radio buttons or a combobox.
For a full explanation of how this is used, see above.
required
required
affects whether the field will be optional or required. If
a field is required, it will be marked with a red asterisk, and input
validation will be enforced to make sure the user provides a value.
If the user skips a non-required field, the variable will be blank for
text-based fields, 0.0
for number
and currency
fields, 0
for
integer
fields, and None
for multiple-choice and yes/no fields.
Some datatype
s are never marked with a red asterisk. For example,
range
and yesno
fields are set to real
values by default, so the user cannot actually skip the question.
The value of required
can be True
or False
. By default, all
fields are required, so you never need to write required: True
unless you want to.
Instead of writing True
or False
, you can write a Python
expression. This expression will be evaluated for whether it turns
out to be true or false. For example, instead of True
or False
,
you could use the name of a variable that is defined by a yesno
question.
Instead of using a true/false variable, you could use a conditional
expression such as favorite_fruit == 'apple'
.
Note that the Python expression is evaluated on the server, before the screen loads in the browser. Whether a field is required or not cannot be controlled in real time when the user is looking at the screen.
disabled
If the disabled
field modifier is set to True
or to a Python
expression that evaluates to a true value, the field will be displayed
as normal, but will be disabled.
When the field is disabled
, this also has the effect of required:
False
.
When the user presses the Continue button, the variable associated with the field will be ignored; it will not be defined or changed.
Note that if the interview logic has arrived at the question
because
it needs the value of a variable that is listed as a field under
fields
in the question
, but you have disabled
the field, the
interview logic will ask the question
again after the user presses
Continue. You can avoid this problem by defining the variable with a
different block, and using a different variable name for the disabled
field.
Note that the disabled
modifier is unrelated to the disable if
,
js disable if
, and disable others
modifiers.
under text
You can guide users as to how they should fill out a text field by
displaying text under the field. You can use Mako templates within
under text
.
hint
You can guide users as to how they should fill out a text field by
showing greyed-out text in a text box that disappears when the user
starts typing in the information. In HTML, this text is known as the
placeholder. You can set this text for a text field by setting
hint
. You can use Mako templates within hint
s.
The hint
is also used to provide the default text the user sees when
they fill out a multiple-choice dropdown or a combobox
input
element within a fields
question.
help
You can provide contextual help to the user regarding the meaning of a
field using the help
field modifier. A question mark icon can be
clicked on to show the help
text in a popup. You can use Mako
templates within help
text.
default
You can provide a default value to a field using default
. You can
use Mako templates in default
text.
choices
The choices
field modifier is used with multiple-choice fields. It
must refer to a list of possible options. The list can be a list can
be a list of plain text items (in which case the label and the
variable value are the same) or a list of key/value pairs (in which the
key is the label seen by the user and the value is the value to which
the variable will be set).
When the datatype
is object
, object_radio
, or
object_checkboxes
, choices
indicates a list of objects from
which the user will choose. For more information about using objects
in multiple choice questions, see the
section on selecting objects, below.
code
If you have a multiple-choice question (radio buttons, checkboxes,
dropdown) and you want to reuse the same selections several times, you
do not need to type in the whole list every time. You can define a
variable to contain the list and a code
block that defines the
variable.
Adding code
to a field makes it a multiple-choice question. The
code
itself refers to Python code that generates a list of
possible options for a multiple choice field. The code
field
modifier is used in place of a choices
field modifer, which you
would use to specify the choices manually.
The Python code runs at the time the question is asked. Therefore,
you can use the code
feature to create multiple-choice questions
that have dynamically-created lists of choices.
The Python code needs to be a single Python expression (which can be a simple variable, or something more complex, like a list comprehension). The result of the expression can take several forms.
It can be a list of single-item dictionaries, as in the example above.
It can be a dictionary (in which case you cannot control the order of items):
It can be a list of text items (in which case the values and labels will be the same):
It can be a list of two-element lists:
You can specify a default by including a three-element list where the
third element is True
if the choice should be selected by default.
You can include “help text” for a choice by including a fourth element in one of the lists, where the element contains the help text you want to be available. The user can see the help text by touching the question mark button.
If your code is a list of tuples, it will be treated the same as a list of lists.
If your code is a list of dictionaries, you can include a
'default'
key in the dictionary indicating a true or false value
that represents whether the choice should be selected by default.
Similarly, you can include help text in a list of dictionaries by
including a 'help'
key in the dictionary indicating the help text
that should be available to the user.
Instead of specifying the choices using key-value pairs where the keys
are what the variable is set to and the values are the labels, you can
use keys label
and value
to reference the label and the
corresponding variable value.
exclude
If you build the list of choices with code
, you can exclude items
from the list using exclude
, where the value of exclude
is
Python code.
In this example, the value of exclude
is a single variable. If
given a list of things, it will exclude any items that are in the list.
none of the above
If you use datatype: checkboxes
, then by
default a “None of the above” choice is added.
You can turn off the “None of the above” choice by setting the
none of the above
option to False
.
You can also change the phrase from “None of the above” to something
else, even a Mako expression. Just set none of the above
to the
text you want to be displayed.
If you use datatype: object_radio
, you can use
none of the above
in the same way. If the user selects the “none of
the above option,” the variable will not be defined when the user
presses Continue.
This option can be useful when you are using the disable others
feature:
You can also use datatype: object_radio
and none
of the above
in combination with show if
:
all of the above
If you use datatype: checkboxes
, you can
optionally include an “All of the above” option.
You can change the phrase from “All of the above” to something else,
even a Mako expression. Just set all of the above
to the text you
want to be displayed.
shuffle
shuffle
can be used on multiple-choice fields (defined with
code
or choices
). When True
, it randomizes
the order of the list of choices; the default is not to “shuffle” the
list.
show if
You can use the show if
field modifier if you want the field to be
hidden under certain conditions. There are three methods of using
show if
, which have different syntax.
Using the first method, the field will appear or disappear in the web
browser depending on the value of another field in the fields
list
that is visible on the screen. Under this method, show if
refers to
a YAML dictionary with two keys: variable
and is
, where
variable
refers to the variable name of the other field, and is
refers to the value of the other field that will cause this field to
be shown.
This can be useful when you have a multiple-choice field that has an “other” option, where you want to capture a text field but only if the user selects the “other” option.
Note that you can only use this syntax to refer to other fields on the
screen; you cannot refer to arbitrary Python variables in your
interview answers. This method of show if
is JavaScript-based, and
takes place in the browser. The interview answers are in Python, on
the server. The web browser does not have access to all of the
Python variables in the interview answers; it only has access to the
values of fields that are displayed in the user interface.
The second method is like the first, but is a shorthand syntax for the
special case where the other field in fields
is a yes/no variable.
Under this method, show if
refers to the other field’s variable
name. If that yes/no input is set to a “yes” value, the field will be
shown, and otherwise the field will be hidden.
As with the first method, the variable name referred to by show if:
must be a variable name associated with a field on the screen (listed
under fields
); it cannot refer to any arbitrary Python variable.
Note that if show if
refers to a field that is itself hidden by a
show if
, then the condition is considered to be false.
Under the third show if
method, the field is either shown or not
shown on the screen when it loads, and it stays that way. You can use
Python code to control whether the field is shown or not. Unlike
the first method, you are not limited to using variables associated
with fields in the fields
list; you can use any Python code;
however, you cannot refer to any of the variables that are defined by
the current question. Under this method, show if
must refer to a
YAML dictionary with one key, code
, where code
contains Python
code. The code will be evaluated and if it evaluates to a positive
value, the field will be shown.
With all of these methods, if any field is not visible on the screen
when the user presses the Continue button, no variable will be set to
anything for that field; it as if the field was never part of the
question
. Therefore, you should always make sure that your
interview logic (including a document that your interview logic
assembles) does not expect these hidden fields to have a definition.
For example, suppose you have this question:
Suppose your interview assembles a document that contains this content:
Favorite fruit: {{ favorite_fruit }}
Favorite apple: {{ favorite_apple }}
In this case, you may find that when favorite_fruit
is Orange
or
Peach
and you press Continue, you will end up back at the same
screen again. This is because your document is requiring a definition
of favorite_apple
. You may have assumed that favorite_apple
will
be defined as the empty string, but that is not how it works.
The way to fix this is to put your logic into the document:
Favorite fruit: {{ favorite_fruit }}
Favorite apple: {% if favorite_fruit == ‘Apple’ %}{{ favorite_apple }}{% else %}N/A{% endif %}
This way, your interview logic will not include the value of
favorite_apple
unless it is applicable.
You may be tempted to write something like this:
Favorite apple: {% if defined(‘favorite_apple’) %}{{ favorite_apple }}{% else %}N/A{% endif %}
However, this is a bad practice that will lead to problems. For
example, if your users revise their answers, the interview answers
could reach a state in which favorite_apple
is defined but
favorite_fruit
is not Apple
, in which case it would be
inappropriate to display the favorite_apple
in the document. Or,
the user might change favorite_fruit
from Orange
to Apple
, in
which case favorite_apple
would be undefined even though it should
be defined. If you weren’t using defined()
, the assembly of your
document would have ensured that the favorite_apple
question would
be asked. Always base your interview logic on actual facts, not the
defined-ness of variables.
If you need to set a default value of a field that could be hidden by
a show if
, you can specify a code
block following the question
:
The depends on
modifier will ensure that favorite_apple
is
invalidated if and when the value of favorite_fruit
changes.
Note that the first and second methods (as well as the js show if
methods discussed below) are JavaScript-based (“client side”), while
the third is Python-based (“server side”). The client-side
JavaScript code context is only aware of fields that exist on the
screen in the user’s web browser, not the variables in the interview
answers; the user’s browser does not know the values of all the Python
variables in the interview answers. Conversely, the server-side
Python context is only aware of the interview answers, and is not
aware of the values of fields on the screen.
The show if
field modifer is not intended to be used as a primary
mechanism of controlling interview logic; it is more of a feature
for customizing the user interface. Thus whatever logic you express
in show if
will probably have to be repeated elsewhere. If instead
of using show if
you gathered the field in a separate question
,
you would only need to specify the logic in one place.
hide if
This works just like show if
, except that it hides the
field instead of showing it.
hide if
cannot be combined with show if
.
enable if
and disable if
The enable if
and disable if
field modifiers work just like show
if
and hide if
, except that instead of visibly hiding the fields
and labels, it disables the input elements.
The use of code
inside of enable if
and disable if
is not
supported. The disabled
modifier allows you to cause a field to be
disabled based on a Python expression.
enable if
and disable if
cannot be combined with show if
or
hide if
on the same field.
js show if
Sometimes you might want to do more complicated evaluations with
on-screen variables than you can do with show if
and hide if
.
When you use the show if
and hide if
field modifiers to refer to
fields that are on the screen, you are able to test whether the fields
are true, or have particular values, but you cannot do anything more
complex, such as test whether the value is one of two values, or the
values of two fields. You also can’t combine show if
, hide if
, and
disable if
on the same field.
The js show if
and js hide if
features allow you to use any
arbitrary JavaScript expression to determine whether a field should
be shown or not. In these expressions, the special JavaScript
function val()
is used to obtain the values of fields. Given the
name of an on-screen field as a string, the val()
function
returns the current value of that field.
JavaScript is its own complete language with different syntax than Python, but with some similarities.
- Instead of
and
, use&&
- Instead of
or
, use||
- Instead of
==
, use===
(==
will often work as well but may have subtle differences) - Just like in Python, you can group expressions with parentheses
()
- Instead of
True
,False
andNone
, JavaScript hastrue
,false
, andnull
as well asundefined
The string that is passed to val()
must perfectly match the
variable name that is used in the underlying question
.
You can use any JavaScript expression that evaluates to true
or
false
with the js show if
feature, but your expression must
contain at least one val()
reference to a field that is actually on
the screen. docassemble scans your expression for the use of
val("some_variable")
in order to know which on-screen variables need
to be monitored for changes. If your expression does not use val()
to refer to a field that is actually on the screen, docassemble
will not be able to tie the field to the appropriate event triggers,
and the field will always be hidden.
The variable mentioned inside val()
must be a literal string to tell
Docassemble to monitor it. Your expression is parsed, but is not
evaluated, when determining what fields your expression references
with val()
. Thus, if you pass something other than a literal
string to val()
, you may find that the showing or hiding is not
triggered, even though val()
would return the appropriate value.
It is possible to write a js show if
that is not actually
conditional on the value of a variable on the screen, but you still
need to reference a field on the screen. For example, your condition
could be someCondition && (true || val("variable"))
(where
variable
is the name of a field on the screen). This JavaScript
expression will be evaluated when the screen loads and whenever the
value of the variable
field changes.
js hide if
This works just like js show if
, except that it hides
the field instead of showing it.
js enable if
and js disable if
The js enable if
and js disable if
field modifiers work just like
js show if
and js hide if
, except that instead of visibly hiding
the fields and labels, it disables the input elements.
js enable if
and js disable if
cannot be combined with js show
if
or js hide if
on the same field.
disable others
If disable others
is set to True
, then when the user changes the
value of the field to something, all the other fields in the question
will be disabled.
Alternatively, disable others
can be set to a list of variables on
the same screen that should be disabled.
note
The value of note
is Markdown text that will appear on the screen.
This is useful for providing guidance to the user on how to enter
information.
If the note
is by itself as its own “field” in the list of fields
,
the text appears along with the other fields:
However, if the note
is used as a field modifier, the note will
appear to the right of field on wide screens. On small screens, the
note will appear after the field:
On wide screens, the location of each note
s is based on the location
of the field itself. This means that if you have note
s on two
adjacent fields, and one of the note
s is lengthy, the note
s could
overlap on the screen. Therefore, make sure to keep your notes short.
html
html
is like note
, except the format is expected to be
raw HTML. It can be used in combination with the css
and
script
question modifiers.
If html
is by itself as its own “field” in the list of fields
, the
HTML will appear along with the other fields:
However, if the html
is used as a modifier for a field, the HTML
will appear to the right of field on wide screens. On small screens,
the HTML will appear after the field:
raw html
raw html
is like html
, except that the HTML is not placed
into a <div>
; it is simply inserted into the page without any
enclosing elements. This allows you to alter the structure of the HTML
in the list of fields.
The help
and under text
modifiers have no effect on a raw html
field. If raw html
is used as a modifier for a field, it will act
just like html
.
Note that when using the tabular
form of a review
list, the raw
html
will go into the <tbody>
of the <table>
, where a <tr>
would normally be placed. Otherwise, the raw html
will be inserted
into the <form>
element.
no label
If you use no label
as the label for your variable, the label will
be omitted. On wide screens, the field will fill more of the width of
the screen if the label is set to no label
.
To keep the width of the field normal, but have a blank label, use
""
as the label.
It is generally a good idea to always use a label for every field,
especially if some of your users may be using screen readers. If you
are inclined to use no label
because you want the field to be wider,
consider using label above field
.
css class
If you specify a css class
, then the HTML input element will have
the specified class, and the <div>
containing the field will have the same
class, except with -container
appended to it.
In this example, the contents of fruit.css
are:
Mako can be used inside css class
.
label above field
If you set label above field
to True
, then the label will be
positioned above the field and not to the left of it. (By default, on
larger screens, the label is positioned to the left of the field.)
You can set label above field
to True
, False
, or a Python
expression.
You can use the labels above fields
feature to make this the
default setting for all fields in your interview.
floating label
If you set floating label
to True
, then the label will be
formatted using Bootstrap’s floating labels style.
You can set floating label
to True
, False
, or a Python
expression.
You can use the floating labels
feature to make this the default
setting for all fields in your interview.
grid
Using the grid
field modifier, you can place fields side-by-side on
the screen. docassemble uses the grid system of Bootstrap,
which is based on flexbox, to implement this. Instead of
representing widths as percentages between 0 and 100, the grid
system uses numbers from 1 to 12.
In the above example, grid: 7
means 7/12ths of the width of the
enclosing HTML element, and grid: 5
means 5/12ths of the width of
the enclosing HTML element. The enclosing element here is the central
column on the screen. Since 7+5=12, the two fields together fill the
width of the central column.
If you specify grid
values for adjacent fields, the fields will be
placed together in the same Bootstrap “row.” If the sum of grid
widths of adjacent fields add up to a value greater then 12, the items
will wrap.
If you are using the grid
field modifier, it is recommended that you
use labels above fields
in features
or the label above
field
field modifier. The default labeling style, where the label is
to the left of the field, can work with grid
, but the label takes up a
width of 4, which does not leave a lot of room for the field.
It is possible to use a different width for the label. To do this,
use grid
to specify a YAML dictionary instead of an integer.
Inside this dictionary, width
is the width of the field itself, and
label width
is the width of the label. Note that writing:
is equivalent to writing:
By default, any adjacent fields that specify a grid
will be joined
together in the same “row.” If you want adjacent fields to be in
separate rows even though they both specify a grid
, you can specify
start: True
or end: True
to indicate that a “row” should start or
end with the given field.
This interview uses start: True
to indicate that the fields A
,
B
, C
, etc. should start on a new row:
The following interview has the same appearance, but uses end: True
to indicate that the row of fields 1
through 10
should end at
field 10
.
Bootstrap’s grid system is responsive. On screens that are less
than 768 pixels wide, grid
fields are arranged vertically. The
threshold of 768 pixels is based on Bootstrap’s “medium”
breakpoint, which uses the code md
.
The breakpoint of 768 pixels can be changed globally for the server by
setting the grid breakpoint
setting of the grid classes
directive in the Configuration. The available values are:
xs
sm
md
lg
xl
xxl
If xs
is used as the grid breakpoint
, that means that grid
fields will be side-by-side no matter how small the screen is.
The breakpoint can also be configured on a field-by-field basis by
setting the breakpoint
option under grid
:
In the above example, the fields for gathering the address will be
side-by-side as long as the screen is at least 576 pixels wide (the
sm
threshold).
If you want to insert horizontal space before a field, set the
offset
under grid
:
This will indent the field by the given amount of space.
You can specify the width
, label width
, offset
, start
, and
end
as Python expressions, and you can specify the breakpoint
using Mako.
If grid
refers to a string, the string is expected to be a Python
expression that evaluates to an integer between 1 and 12.
item grid
The item grid
field modifier is similar to the grid
field
modifier, but it only applies to fields that contain a list of radio
buttons or a list of checkboxes.
The default breakpoint is the md
screen size. You can change this
globally for the server using the item grid breakpoint
setting
under the grid classes
Configuration directive. You can change
this for a particular field by setting item grid
to a YAML
dictionary with values width
and breakpoint
.
label
and field
Instead of expressing your labels and variable names in the form of -
Label: variable_name
, you can specify a label using the label
key
and the variable name using the field
key.
field metadata
The field metadata
field modifier allows you to associate custom
metadata with a field. You can use any format YAML will accept, and
you can use Mako in text. The metadata will appear within the JSON
representation of the question
.
Special features
When the list of choices is empty
If the list of choices for a multiple choice question is empty,
docassemble will try to deal with the situation gracefully. If
there is only a single field listed under fields
, or the question is
a standalone multiple choice question, then the
variable that will be set by the user’s selection will be set to
None
, and the question (or the field, if there are other fields
listed under fields
) will be skipped.
If the datatype
is checkboxes
, the variable will be set to an
empty DADict
(a type of dictionary specific to docassemble).
If the datatype
is object_checkboxes
, the variable will be set to
an empty DAList
(a type of list specific to docassemble).
Input validation
Some datatypes, such as numbers, dates, and e-mail addresses, have validation features that prevent the user from moving to the next page if the input value does not meet the requirements of the data type. The jQuery Validation Plugin is used.
For some field types, you can require additional input validation by adding the following to the definition of a field:
min
: forcurrency
andnumber
data types, require a minimum value. This is passed directly to the jQuery Validation Plugin.max
: forcurrency
andnumber
data types, require a maximum value. This is passed directly to the jQuery Validation Plugin.
minlength
: require a minimum number of characters in a textbox, number of checkboxes checked, etc. This uses the jQuery Validation Plugin.maxlength
: require a maximum number of characters in a textbox, number of checkboxes checked, etc. This uses the jQuery Validation Plugin.
The min
, max
, minlength
, and maxlength
specifiers accept
Mako, so you can use Mako templating if you need computable
validation limits.
You can customize the standard
validation messages that users see. If you want to customize these
messages on a server-wide basis, you can edit the words
directive
in the Configuration. If you want to customize these messages on an
interview-wide basis, you can add a default validation messages
block to your interview. For more information on how to do this, see
the documentation for the default validation messages
block.
You can also customize the messages for a particular field using the
validation messages
field modifier.
Each validation error message has a special code. In the example
above, the codes were required
and max
. A full list of these
codes is available in the documentation for the default validation
messages
block.
In a validation messages
field modifier, you can use an abbreviated
version of many of these codes. For example, instead of using the
code combobox required
, you can use required
. Instead of using
date min
, you can use min
. Either will work in the context of a
validation messages
field modifier. The last word in the code is
sufficient.
You can also use Python code to validate an
input field. To do so, add a validate
field modifier that refers to
the name of a function that returns True
(or something that
Python considers “true”) if the value is valid, and False
(or
something that Python considers “not true”) if the value is invalid.
In this example, the function is_multiple_of_four
is defined as
follows:
This Python code is in the validationfuncs.py
file. The
modules
block includes this code. The function returns True
if 4
divides the input value into a whole number
The error message that the user will see is a generic error message,
“Please enter a valid value.” In most cases you will want to explain
to the user why the input did not validate. To provide a more
descriptive error message, your function can call the
validation_error()
function with the error message the user should
see.
In this example, the function is_multiple_of_four
is defined as
follows:
This Python code is in the validationfuncstwo.py
file. If 4
does not divide the input value into a whole number, then
validation_error()
is called. The validation_error()
function
raise
s an exception, which means that code stops processing once
the validation_error()
function is called. That is, if
validation_error()
is called, the return True
statement will not
be executed.
The text passed to validation_error()
is the text the user will
see if the value does not validate. If 4 does divide the input value
by a whole number, the function returns True
, which indicates that
the input is valid.
Instead of creating a separate module file, you can also use an anonymous
(Lambda)
function as the value of the validate
field modifier. This may be useful if your
validate
function is very simple. It is common to use x
as the variable name in
a Lambda expression, but note that this is a reserved name in docassemble, so you
should use a different variable name, such as y
.
Note that the validate
field modifier is not available for use with
fields having datatype: checkboxes
. (However, note that you can use
minlength
and maxlength
to require a certain number of
checkboxes to be checked when none of the above
is disabled.)
A more general limitation of these validation functions is that they can only test for characteristics inherent in the variable being validated; they cannot compare the variable to other variables.
You can get around this restriction
using validation code
. Rather than showing an inline validation
error, validation code
will create a pop-up error for the question
as a whole.
Note that the code under validation code
is not within a function,
so it should not try to return
any values. If the code runs through
to the end, this indicates that the input for the question is valid.
If validation_error()
is called, or an exception is raised, the
input for the question is considered invalid.
If the input is invalid, the user will see a message at the top of the
screen containing the error message passed to validation_error()
,
or the error message for the error that was otherwise raise
d.
In addition to validating user input by raising an exception if
something is wrong, you can use validation code
to transform values
before they are saved in the interview answers.
For example, this validation code
normalizes the formatting of a
phone number.
The following validation code
makes adjustments to object attributes if a
user’s income is less than zero.
By default, an error message raised by validation code
is placed at
the top of the screen. If you want the message to be placed next to a
specific field on the screen, you can call validation_error()
with
the optional keyword argument field
set to the name of the field.
Code under validation code
is very different from code in a code
block. It is not interview logic and cannot function as interview
logic; it can only be used to validate or transform user input before
the interview answers are updated. If you refer to an undefined
variable in validation code
, docassemble will not try to fetch
the definition for you, as it normally does; instead, the user will
see an error. You cannot use functions like force_ask()
or
command()
, which operate by raising exceptions. If you try to make
validation code
do the work of interview logic, you will be
disappointed.
Address autocomplete
If you have defined a google maps api key
in the Configuration,
you can use the Place Autocomplete feature of the
Google Places API to help your users enter addresses. Address
suggestions will be provided as the user begins to type. To use this
feature, modify the street address (.address
) field by setting
address autocomplete
to True
.
You can set address autocomplete
to True
, False
, or a Python
expression that returns True
or False
.
For more information on using this feature, see the documentation for
the Address
object.
This feature can be used internationally with a variety of address
types. Here is an example that illustrates all of the possible
attributes of the Address
object that can be set by Place Autocomplete.
Advanced usage
If you want to use additional features of the Place Autocomplete
JavaScript API that are not, you can set address autocomplete
to a
dictionary of options that will be passed directly to the Place
Autocomplete API.
You will need to set the types
and fields
items within the
dictionary to values that the Place Autocomplete API considers
valid. Consult the API documentation for the list of valid types and
fields. docassemble will pass the dictionary of options directly
to the JavaScript for the Place Autocomplete API without checking if
the options are valid. You need to monitor the JavaScript console and
consult Google’s documentation if you do not get valid results.
The following example demonstrates conducting a query on “establishments” of all types.
Note that there are some fields that the API returns, such as
open_hours
, that docassemble will not process. The above example
demonstrates all of the fields that docassemble is capable of
populating. Specifying the geometry
field allows the latitude
and
longitude
fields to be populated. Specifying the
address_components
field allows the basic address fields to be
populated.
Note that in the above example, address autocomplete
is attached to
the name
attribute of the Address
rather than the address
attribute. You can attach address autocomplete
to any text field,
whether or not it corresponds to a field that is re-written by
JavaScript. However, you must attach address autocomplete
to an
attribute of the object whose attributes you wish to populate
(typically this is an Address
object).
Setting address autocomplete
to True
passes the following options
to the Place Autocomplete API:
The following example demonstrates conducting a query on establishments of particular types.
The following example demonstrates using the (cities)
type.
The following example demonstrates using the (regions)
type.
Instead of specifying the dictionary of options in YAML, you can set
address autocomplete
to a Python expression that returns a
dictionary of options.
Note that API calls to the Place Autocomplete API are more expensive depending on the type of search done and the fields that are returned.
Setting a variable with the Continue button
Sometimes, it is useful for a question
to set a single variable to
True
, along with the other variables it sets, much like the simple
“continue” button that sets a variable question does.
If you want your question
to set a variable to True
when the user
presses “Continue,” add a continue button field
line to the
question
indicating the variable that should be set to True
.
Assigning existing objects to variables
Using Mako template expressions (Python code enclosed in ${ }
), you can
present users with multiple-choice questions for which choices are
based on information gathered from the user. For example:
But what if you wanted to use a variable to refer to an object, such as a person? You could try something like this:
In this case, tallest_person
would be set to the name of the
client
or the name of the advocate
. But what if you wanted to
then look at the birthdate of the tallest person, or some other
attribute of the person? If all you had was the person’s name, you
would not be able to do that. Instead, you would want
tallest_person
to be defined as the object client
or the object
advocate
, so that you can refer to tallest_person.birthdate
just
as you would refer to client.birthdate
.
You can accomplish this by setting datatype
to object
within a
fields
list, where the choices
are the names of the
objects from which to choose. (Optionally, you can set a default
value, which is also the name of a variable.)
For example:
Note that this interview incorporates the basic-questions.yml
file
which defines objects that are commonly used in legal applications,
including client
and advocate
. It also contains questions for
asking for the names of these people.
The interview above presents the names of the client
and the
advocate
and asks which of these people is the villain.
If the user clicks the name of the advocate, then docassemble will
define the variable villain
and set it equal to advocate
.
Note that because advocate
is an object, villain
will be an
alias for advocate
, not a copy of advocate
. If you
subsequently set advocate.birthdate
, you will immediately be able
retrieve that value by looking at villain.birthdate
, and vice-versa.
Also because villain
is an alias, if you refer to
villain.favorite_food
and it is not yet defined, docassemble
will go searching for a question that offers to define
advocate.favorite_food
. This is because docassemble objects
have an intrinsic identity, a unique name given to them at the time
they are created. (You can inspect this by referring to
villain.instanceName
in a question and will see that it returns
advocate
.) For more information about this, see the discussion in
the documenation for DAObject. (All docassemble objects are
subtypes of DAObject.)
If any of the objects listed under choices
represent
lists of objects, such as case.defendant
or client.child
(objects
of type PartyList
, those lists will be expanded and every item will
be included. You can also include under choices
Python code, such
as case.parties()
or case.all_known_people()
.
The datatype
of object
presents the list of choices as a
pull-down. If you prefer to present the user with radio buttons, set
the datatype
to object_radio
. The object_radio
data type
allows the use of a none of the above
option.
By default, the objects listed in the
user interface are labeled by their textual representations. For
example, if the object in a choices
list is an Individual
, the
label for the object will be the textual representation for an
Individual
, which is the individual’s name. To use an alternate
label, provide a object labeler
. The object labeler
must be a
Python expression that evaluates to a function.
For example:
In this case, the protagonist
and the antagonist
will be labeled
using the nickname
attribute. The object labeler
in this example
is a Python lambda function, which is a shorthand way of creating a
function. You could also used a named function, if you wrote one in a
module. For example, suppose you had some code in a module that
defined the function my_labeling_function
:
Suppose also that you imported this function into your interview using
a modules
block. Then, in your fields
item you could simply
write object labeler: my_labeling_function
.
Here is an example that uses object labeler
to label a datatype:
object
list of addresses.
Instead of writing:
you could instead write:
Address.on_one_line
(note the lack of parentheses at the end) is a
reference to the .on_one_line()
method of the Address
class. In
Python, a method is like a function where the first parameter is the
object. So you can call Address.on_one_line
like a function,
passing it the object instance as a parameter.
Using a reference to a method in place of a lambda function only works
if the method has no other required parameters. For example, if your
objects were Individual
s and you wanted the the choices to
display as “John Smith’s house,” “Jane Doe’s house,” and “Harry
Morgan’s house,” you could write:
but there would be no way to call this method successfully using a mere method reference like
because there is no place to put the 'house'
parameter.
Similar to
the way object labeler
works, you can specify a help generator
lambda function that takes the object as its argument and returns help
text associated with a choice. You can also specify an image
generator
lambda function that generates the image that should be
associated with the choice.
Embedding fields within a paragraph
Within a fields
question, you can include fill-in fields within
the text of the subquestion
using markup of the form
[FIELD variable_name]
.
Any variable name referenced in [FIELD ...]
must be one of the
variable names listed in the fields:
list. If a field is referenced
this way in the subquestion
, it will not be displayed the way that
fields are ordinarily displayed, but will be moved into the
subquestion
, where it will be formatted differently. Any fields
in the fields:
list that are not referenced in the subquestion
will appear on the screen in the normal fashion.
The label of an embedded field is used as the tooltip of the field.
When you are using embedded fields, you can
add the field modifier inline width
to change the initial width of
the field. For example, if you include inline width: 15em
, the
CSS will be altered so that the field is 15em wide. This field
modifier has no effect when embedded fields are not being used.
Generating fields with code
You can use Python code to generate items inside a fields
. To do
so, simply add an entry under fields
that contains code
(and
nothing more). The contents of code
will be evaluated as a Python
expression.
The expression must evaluate to a list of dictionaries, and the format
must be the Python equivalent of a regular fields
item, which you
would normally express in YAML.
For example, if you want the fields to be like this:
you would write this:
Here is an example that asks for the names of a number of people on a single screen:
Note that it is necessary to use the sets
modifier on the question
to manually indicate that the question will define
people[i].name.first
. Normally, docassemble automatically
detects what variables a question is capable of defining, but when the
fields
are dynamically generated with code, it is not able to do so.
Note also that this example uses the label
and field
method for
indicating the label and the variable name for each field. This is
not required, but it may make field-generating code more readable.
Dynamically-created lists of fields can be paired with
dynamically-created subquestion
text that embeds the fields.
It is also possible to mix dynamic fields with non-dynamic fields:
Writing Python code that generates a list of fields can be pretty
complex. This should be considered an advanced feature. Note that
the code above uses the Python function str()
to reduce the
index of a list (which is an integer) into a string, for purposes of
constructing variable names like people[0].name.first
and
people[1].name.first
.
If you work with dictionaries (DADict
objects) instead of lists
(DAList
objects), a useful function is the Python function
repr()
, which returns a string containing a string with quotation
marks around it.
For example, suppose you want to replicate this:
You could do something like the following:
The alternative is to try to provide the quotation marks manually,
which can look messier, and then you have to worry about what to do if
the key
string contains an apostrophe; will that cause a syntax
error? The repr()
function takes care of this problem by
producing a robust Python representation of the string.
A comprehensive example
Here is a lengthy example that illustrates many of the features of
fields
.
Questions that upload files
Storing files as variables
Users can upload files, and the files are stored as a variable in docassemble.
Note that this question uses fields
, which is explained in more
detail above. Specifically, it uses the file
data type.
When set, the variable user_picture
will be a special object of
type DAFileList
. For more information about how to make use of
uploaded files, see inserting images.
Note that after a file is uploaded, if you send the user back to the
same question
again, the user might expect to see the file that they
had already uploaded. However, they will instead be required to upload
a new file. This new upload will replace the DAFileList
they had
created early.
To see an example of providing the user with an interface for editing a list of files that were upload (including deleting specific files, reordering files, and adding additional files), see this recipe.
Gathering the user’s signature into a file variable
The signature
block presents a special screen in which the user can
sign his or her name with the trackpad or other pointing device. When
the user presses “Continue,” the signature image will be uploaded to
the docassemble server as a transparent PNG file.
On the screen, the question
text appears first, then the
subquestion
text, then the signature area appears, and then the
under
text appears.
In this example, the user_signature
variable will be set to an
object of type DAFile
. This variable can be included in the same
way that a document upload can be included. For example:
or, if you want to control the width of the image:
Signatures can be also be inserted into assembled documents in the same way. They can also be inserted into DOCX fill-in forms and PDF fill-in forms.
On a small screen, users need as much of the screen as possible to
write their signature. For this reason, docassemble will reduce
the size of the navigation bar and put the question
text into the
navigation bar. For this reason, you should make sure your
question
text is very brief – no longer than “Sign your name.”
You should also make the subquestion
text as brief as possible.
Although you may be developing your app on a desktop or laptop
monitor, your users are probably using smartphones, so test your app
on a small smartphone.
By default, the signature
screen will not let the user continue if
the signature box is empty. If you want to allow users to submit
blank signatures, set required
to False
:
By default, the color of the signature is
black. To use a different color, set pen color
to a valid CSS
color. This example uses a blue color.
Mako can be used when specifying a pen color
.
Generalizing questions
docassemble lets you write a single question that can be re-used throughout an interview.
For example, suppose you want to gather the following variables:
spouse.birthdate
mother.birthdate
father.birthdate
or:
plaintiff[0].served
plaintiff[1].served
plaintiff[2].served
It would be tedious to have to write separate questions for each of these variables.
Luckily, there are two features in docassemble that allow you to
write questions (and other blocks that set a variable) in a
generalized way: the generic object
modifier, and index
variables.
The generic object
modifier
The generic object
modifier is explained more fully in the
section on question modifiers, but here is an example:
The special variable x
stands in for any object of type
Individual
.
If you are not yet familiar with the concept of “objects,” see the objects section.
The generic object
modifier can be used with question
blocks,
code
blocks, and any other blocks that set variables
(template
, table
, attachment
, and objects
, objects
from file
, data
, data from code
).
Index variables
If you have an object that is a type or subtype of DAList
or
DADict
, you can refer generically to any item within the object
using an index variable.
The special variable i
will stand in for the index
of whichever list member your interview asks about.
You can nest iterators up to six levels, using the variables i
,
j
, k
, l
, m
, and n
, but you have to use them in this order.
For more information about populating groups of things, see the groups section.
For more information about how docassemble identifies what question to ask in order to define a given variable, see the interview logic section.
Index variables can be used with question
blocks, code
blocks,
and any other blocks that set variables (template
, table
,
attachment
, and objects
, objects from file
, data
,
data from code
).
Tips on using generalized questions
If you use generic object variable x
, or index variables like i
,
j
, k
, etc., it is important that you do not use them in blocks
that you have marked as mandatory
.
Suppose you have a block that defines fruit[i].seeds
. When
docassemble needs a specific value, like fruit[2].seeds
, it will
find your block automatically, no matter where it is in the interview
source file. docassemble will take care of setting i = 2
before
“running” your block. Your block will only work correctly if i
is
set to the right value.
If you mark the block as mandatory
in order to force it to be run,
you will be forcing the running of Python code in a context where
the value of i
could be anything; it might be a number like 0
or
5
, or it might be a string like 'income'
. The variable i
might
not even be defined at all.
Thus, you should only use x
, i
, j
, k
, etc. when you are
letting docassemble choose which block to use.
Catchall questions
By default, if a reference is made to a variable and no block that defines that variable is available, an error message will appear saying “Interview has an error. There was a reference to a variable ‘variable_name’ that could not be looked up in the question file.”
Typically, you should always have a question
or code
block
that defines any variable your interview might encounter. But if you
want to have a fallback option, you can set use catchall: True
in
the features
.
This interview uses the variable names user_name
and salary
, but
there are no blocks that define user_name
or salary
. However,
use catchall: True
is part of the features
. This means that
when the variable user_name
is encountered, user_name
is defined
as a DACatchAll
object. The DACatchAll
class is a subclass of
DAObject
. The instanceName
attribute of the object is set to
'user_name'
. When the interview tries to place user_name
into
Mako text, this has the effect of calling str(user_name)
. Because
of the way DACatchAll
objects work, this results in a call to
str(user_name.value)
; thus docassemble will seek the value of
user_name.value
. The interview provides a generic
object
block that sets x.value
where x
is a
DACatchAll
object.
Thus, with use catchall
, you can have a single question
in your
interview that can define any single variable, no matter what its name
is. In the example above, the first question
uses the
.object_name()
method to present a user-friendly representation of
the variable name based on the .instanceName
attribute of the
object.
One problem with such “catchall” questions is that the data type of
the variable is not known. The DACatchAll
object provides a hint
about the data type where possible. If the variable user_name.value
is sought because str()
is called on user_name
, then
user_name.context
is set to 'str'
. This attribute is available to
your question
block.
In the above example, a second variable is salary
. When the
interview calls currency(salary)
, this has the effect of calling
float(salary)
. This means that when salary.value
is sought,
salary.context
will be 'float'
. The second question
block in
the interview asks the question a different way based on this context,
using an if
specifier.
If user_name + '@example.com'
or currency(salary + 10000.0)
triggers
the seeking of the value
attribute, then the context
attribute
will be 'add'
. This is ambiguous because the +
operator can refer
to string concatenation as well as numeric addition. Luckily, in the
scenario where the catchall variable is followed by an operator like
+
, the operand
attribute is set to the value on the other side of
the operator. You can test for the data type on the other side of the
operator and infer what the data type of the catchall variable should be.
The .context
values are based on whichever of the Python special
methods was called on the variable. The possible values of
.context
are 'abs'
, 'add'
, 'and'
, 'bool'
, 'complex'
,
'contains'
, dir'
, 'div'
, 'divmod'
, 'eq'
, 'float'
,
'floordiv'
, 'ge'
, 'getitem'
, gt'
, 'hash'
, 'hex'
,
'index'
, 'int'
, 'invert'
, 'iter'
, 'le'
, 'len'
, 'long'
,
'lshift'
, 'lt'
, 'mod'
, 'mul'
, 'ne'
, 'neg'
, 'oct'
,
'or'
, 'pos'
, 'pow'
, 'radd'
, 'rand'
, 'rdiv'
, 'rdivmod'
,
'repr'
, 'reversed'
, 'rfloordiv'
, 'rlshift'
, 'rmod'
,
'rmul'
, 'ror'
, 'rpow'
, 'rrshift'
, 'rshift'
, 'rsub'
,
'rtruediv'
, 'str'
, 'sub'
, 'truediv'
, and 'xor'
. Since dates
are not a built-in Python data type, whether the variable is a date
cannot be detected based on the context in which the variable was
accessed. If the .context
is 'bool'
, it is likely that the
variable was used in the context of an if
statement.
If you call .data_type_guess()
on a DACatchAll
object, it will
return 'str'
, 'int'
, 'float'
, 'bool'
, or 'complex'
, based on
what the .context
is and what the .operand
is (if applicable).
The .data_type_guess()
method will likely work correctly most of the
time, but what it returns is just an opinion. In particular, whether
a number should be an int
or a float
is highly debatable.
You may want to implement a convention of embedding the data type in
the variable name, so that you can identify the data type in
situations where the Python special methods do not provide a
reliable answer. For example, you household_size_int
instead of
household_size
, deadline_date
instead of deadline
, or
salary_currency
instead of salary
.
Since it is better for variables to be set to their natural types
rather than as the artificial object DACatchAll
, you will probably
want to use validation code
to overwrite the DACatchAll
object
with a different value. The example above does this by using the
define()
function, obtaining the name of the variable from the
instanceName
. Thus, at the end of the interview, user_name
is a
string, salary
is a floating-point number, and there are no
DACatchAll
objects.
It is possible to use validation code
to try to transform data types
once you know what input the user has provided. For example, if the
user types a valid date into a text box, you can set the variable to a
DADateTime
object:
This works because as_datetime()
will raise an exception if it is
given text that does not contain a valid date. Python’s
try
/except
intercepts the error and sets the variable to the plain
value of the date is not valid.
Note that the utility of the use catchall
feature is very limited.
They are not a replacement for interview YAML.
Special screens
Performing special actions requested by the user
You can allow users to click links or menu items that take the user to
a special screen that the user would not ordinarily encounter in the
course of the interview. You can create such a screen using an
event
specifier.
An event
specifier acts much like sets
: it advertises that the
question will potentially define a variable (although it actually doesn’t).
In the following example, the variable show_date
is never defined;
it is simply sought. The task_not_yet_performed()
function is
used to make sure that the dialog box only appears once.
The event
specifier is important if you use the roles feature to
conduct multi-user interviews.
In the example above, the event
line tells docassemble that this
question
should be displayed to the user if docassemble
encounters the role_event
, which is a special “event” that can
happen in multi-user interviews. The event is triggered when the
interview reaches a point when a person other than the current user
needs to answer a question. For example, while a client is filling
out an interview, the interview logic might call for a variable that
can only be set by an advocate who reviews the client’s answers. In
this scenario, a role_event
will be triggered. When this happens,
docassemble will look for a question
or code
block that
defines the variable role_event
, and it will find the example
question above.
event
can also be used to create screens that the user can reach
from the menu or from hyperlinks embedded in question text. For
information and examples, see url_action(), process_action(),
action_menu_item(), and menu_items.
However, event
is not appropriate for question
s that set variables
(e.g., that use yesno
, noyes
, field
, continue button field
,
fields
, signature
, etc.). If you want to take the user to a
screen that sets a variable, refer to an undefined variable so that
docassemble will seek out the definition of the variable and show
the question
that defines the variable. Or, if the variable is
already defined, use force_ask()
. The interview logic system in
docassemble is not like a flow chart, where you “go to” question 1
and then “go to” question 2; it is based on seeking definitions of
variables and satisfying prerequisites.
The event
modifier can also be used on code
blocks, where the
meaning is similar, but the purpose is not necessarily to show a
special screen.
Creating a special screen where the user can review his or her answers
The review
specifier allows interview developers to create a
review
screen. A review
screen is type of question
that allows
users to review and edit their answers, whether the user is part of the
way through the interview or all the way through the interview.
Typically, the user will get to this screen by selecting an option
from the web app menu (e.g., “Review Answers”), or by clicking on a
hyperlink within subquestion
text (e.g., “to review the answers you
have provided so far, click here”).
Here is an example of a review
screen that is launched from the
menu:
If you click “Favorite fruit,” you are taken to a question
where
you can edit the value of fruit
. This has the same effect as
calling force_ask()
on 'fruit'
or running an action on
'fruit'
; whatever block in your interview offers to define fruit
will be used. After the user edits the value of the variable, the
user will return to the review
screen again.
Note that the review
screen does not show a link for “Favorite
fungus” because the variable fungi
has not been defined yet.
However, once fungi
is defined, the review
screen would show it.
This behavior is different from the typical behavior of
docassemble blocks. Normally, referring to a variable that has
not yet been defined will trigger the asking of a question that will
define that variable. In the review
screen, however, the presence of
an undefined variable simply causes the item to be omitted from the
display.
For more information about adding menu items, see the sections on special variables and functions.
In the above example, note that the question
with the review
specifier is tagged with event: review_answers
. For more
information about how event
s work, see above. The
interview will show this screen whenever it seeks out the definition
of the variable review_answers
. Since the screen is displayed based
on an event
, it can be called as many times during the interview
session as the user likes. Depending on which variables have been
defined, the user will see different things.
Customizing the display of review
options
You can provide the user with a list of answers the user has provided with buttons that the user can press to revisit an answer:
The review
specifier, like the fields
specifier, allows you
to use note
and html
entries.
If these are modified with the optional show if
field modifier, they
will only be displayed if the variable referenced by the show if
field modifier has been defined. In addition, if any of these entries
refer to a variable that has not been defined yet, they will be
omitted.
By default, items in a review
list have the CSS class of
bg-secondary-subtle
so that each item is distinguishable from its
neighbors. However, note
and html
items do not have a class. Using
the css class
modifier on an item, you can change the CSS class of
an item.
In this example, the favorite_vegetable
item has been given a
different background color, and the note
, which by default is
colorless, is given the color bg-secondary-subtle
so that it matches
the other items.
If you want an item to have no background color, set the css class
to the name of a class that does not exist or that does not define a
background color.
If you include note
and html
as modifiers of an item under the
review
specifier, the text will appear to the right of the item on
wide screens. On small screens, the HTML will appear after the item.
You can add help
text to an item, in which case the text is shown
underneath the hyperlink. If this text expects a variable to be
defined that has not actually been defined, the item will not be
shown. Note: this is not available with the button
display format.
If you want the list of review items to be formatted as an HTML
<table>
, set the tabular
modifier on the question
to True
.
The tabular
modifier can also be used to specify a particular CSS
class for the <table>
.
Mako can be used with tabular
.
By referring to a list of variables instead of a single variable, you
can indicate that more than one variable should be sought. The fields
mentioned will not appear on the review
screen until all have been
gathered.
If there is a follow-up question that might need to come after the
changing of a variable, you can list the follow-up variable in the
fields
under follow up
.
You will need to tag the follow-up question with an if
modifier;
in order for the review
screen to skip the field when it is not
required, it needs to find no question
s that will define the
variable. If the follow-up question is set up in this way, you can
list its variable under follow up
, and docassemble will ask the
question if the if
condition is true, but will ignore the follow
up
variable if the if
condition is false.
You can also indicate more than one variable when using show if
:
Some of the variables that you use in your interview might be computed
by code
based on answers to question
s, rather than defined
directly by asking the user a question. Thus, if the user changes the
answers to these underlying questions, you may want your interview to
recompute the values of these variables. This recalculation does not
happen automatically; however, you can cause it to happen in your
review
screen by including recompute
in the list of variables to
be re-asked.
In this example, it would not have worked to merely include the
variable salad
in the list of variables, as follows:
Here, the presence of salad
in this list means “ask a question
to redefine the variable salad
.” If there is no question
that
defines salad
, the interview will generate an error. Including
salad
in a recompute
list, as in the above interview, indicates
that it is ok if the variable is defined by code
.
You might also want to use recompute
with variables that are defined
by code
in some circumstances but are defined by question
s in
other circumstances.
When you write lists of operations to be performed when a user clicks
a link on a review
page, you will probably want to make sure that at
least one of the variables in the list will trigger the asking of a
question
. Otherwise, the user might click the link and be
returned back to the same page again, and when that happens they may
assume that clicking the link didn’t do anything, and the app is
broken.
There are three other special commands that you can use in a list of
variables in a review
item: set
, undefine
, and invalidate
.
The following example illustrates set
:
This interview demonstrates how to re-do the geocoding of an
Address
. When you call .geocode()
on an Address
the
first time, the address is geocoded and the .geocoded
attribute
of the object is changed from False
to True
. If you call
.geocode()
on the object again, the first thing it does is check
the .geocoded
attribute, and if it is True
, it will immediately
return without doing anything. This is useful for avoiding
unnecessary API calls, which can slow down the responsiveness of your
app. However, if the user edits the underlying attributes of the
address, you need to “reset” the geocoding in order to get it to run
again.
In the above interview, the set
command sets address.geocoded
to
False
, which means that when the address.county
is recomputed, and
the .geocode()
method is run again by the code
block, then the
.geocode()
method will actually geocode the new address.
The undefine
specifier causes the values to be undefined. The
invalidate
specifier works like undefine
, except that the original
values (if any) will be remembered and offered up as default values
when a question
defining the variable is asked again.
Placing a review
screen within the interview logic
In the examples above, the question
containing the review
specifier is identified with an event
specifier like event:
review_answers
, meaning that the variable review_answers
does not
actually get defined, though it gets sought.
As a result, a review
screen identified with an event
can only be
shown when triggered by a user action (e.g., clicking a link,
selecting an item from the menu), or with code
.
If you would like to insert a review
screen into the normal course
of an interview, so that it appears to the user one time, you can use
continue button field
instead of event
.
In this example, the variable answers_reviewed
actually gets
defined; it gets set to True
when the user clicks “Continue.” It
works much like a standard question with a “Continue” button that sets a
variable to True
.
The interview flow in this interview is set by the code
block.
First the interview asks about the user’s favorite fruit, vegetable,
and fungus. Then the review
screen is shown. Then the final
screen is shown.
Ensuring variables are defined first
By default, when a review
screen encounters and undefined variable,
it does not seek out its definition. This is so you can have a single
review
screen that is used throughout an interview (or a section of
an interview), where the user only sees the fields that have already
been asked about.
If you would like to use the functionality of a review
screen, but
you want all the variables to be defined first, set skip undefined
to False
:
This enables you to use tables in your review
screen. Ordinarily,
tables are always undefined (so that their contents always reflect the
current state of the list, so a review
screen would never display them.
Customizing the Resume button
By default, the review
screen puts a “Resume” button at the bottom of
the screen. If you want the label on the button to be something other
than the word “Resume,” add a resume button label
modifier.
However, if review
is used with continue button field
, a
“Continue” button is used. The “Continue” button can be customized
using the modifier continue button label
.
For information about other ways to set a default value for the Continue button label, see the screen parts section.
Why can’t review
screens be automatically generated?
The list of variables to display to the user in a review
screen needs
to be specified by the interview developer. There are several reasons
why this needs to be done manually as opposed to automatically:
- Variables in your interview may be interdependent. You do not
necessarily want to allow the interviewee to edit any past answer
at will because this may result in internal inconsistencies or
violations of the logic of your interview. For example, if your
interview has a variable called
eligible_for_medicare
, which is set after the user answers a series of questions, you would not want the user to be able to go back and set his or her age to 30, at least not without a reconsideration of the definition ofeligible_for_medicare
. Therefore, it is important that the interview developer control what the user can edit. - A list of answers already provided might not be user-friendly unless the interview developer presents it in a logically organized fashion. The order in which the questions were asked is not necessarily the most logical way to present the information for editing.