Forms 2.0 - Dynamic Forms
This page is preliminary and subject to change as Forms 2.0 continues to be developed.
A dynamic form decides some of its fields when it loads, instead of having all of them laid out in the Form Builder. You place an empty Dynamic Container on the form, and actions in the form's On Preinit event add fields into it - one action per field, or one action inside a loop that adds a field for every row a SQL query returns.
Use it when the questions themselves live in data: a different set of questions for each event, product, or survey; extra fields only some customers need; or a form whose fields an administrator maintains in a table rather than in the Form Builder.
Choosing the right approach
Fields can only be added while the form is loading. Once the form is on screen, no action can add another field to it - a button or a change to another field can't trigger one. Before building a dynamic form, check whether one of these fits better:
| If... | Use |
|---|---|
| The fields depend on data known when the form loads - who the user is, a value in the URL, rows in a table | A Dynamic Container with fields added in On Preinit (this page) |
| The user decides how many sets of the same fields they need - several addresses, several line items, several attendees | A Repeater. Its entries are added and removed by the user while filling out the form, and it can be preloaded with existing records |
| The possible fields are known in advance, and only some apply depending on the user's answers | Ordinary fields with reactive Visibility settings (see Common Field Settings), shown or hidden as the user fills out the form |
A Repeater is often the better choice when what varies is the number of entries rather than the kind of fields. It's dynamic on the client, with no actions needed to add an entry, and its submitted entries come out as Lists that are easy to loop over and save.
The approaches combine well. A dynamic field can have its own reactive visibility rule, so you can add fields in On Preinit and then show or hide them as the user answers other questions.
How it works
- On Preinit runs. The actions in Settings > Events > On Preinit (see Settings) run before any field is initialized. Each Add ... Field action inserts a new field into the Dynamic Container named in its Container Id.
- Fields initialize. All fields, including the new ones, get their initial values.
- After Fields Initialization runs, and the form works out which fields are active and renders. The new fields appear in the order their actions ran, after any fields placed in the container at design time.
- On submit, the added fields are restored from the form's encrypted state, so they're submitted, sanitized, and validated just like fields placed in the Form Builder. The end user can't add, remove, or change their definitions.
The Add ... Field actions are in the Dynamic Fields group of the action picker. That group is hidden in On Page Load and After Fields Initialization, because by then it's too late to add fields. Use these actions only in On Preinit.
The Dynamic Fields actions require a license that includes the Dynamic Forms feature.
Building a dynamic form
- On the Build tab, drag a
Dynamic Containeronto the Canvas where the dynamic fields should go, and give it a Name - for exampleQuestionsContainer. Set its Layout Mode and Gap as you would for a Layout Container. - On the Settings tab, under On Preinit, click Add Action and pick an action from the Dynamic Fields group, such as Add Text Box Field.
- Set Container Id to the Dynamic Container's Name (
QuestionsContainer), and give the new field a Name and Label. Fill in any other settings the field needs. - Add a button with an On Click Handler that uses the new fields' values - see Using the submitted values.
Field types you can add
| Action | Adds a |
|---|---|
| Add Text Box Field | Text Box |
| Add Textarea Field | Textarea |
| Add Dropdown Field | Dropdown |
| Add Date Field | Date |
| Add Time Field | Time |
| Add Money Field | Money |
| Add Phone Field | Phone |
| Add Rich Text Box Field | Rich Text Box |
| Add Content Block Field | Content Block |
Other field types, such as Number, Password, Button, containers, and Repeaters, can't be added at runtime. Place them in the Form Builder instead.
Settings shared by every Add Field action
Each action also has the type-specific settings of the field it adds. For example, Add Text Box Field has Maximum Length and Mask, and Add Dropdown Field has Data Source. See each field's page for those. Every setting below supports Tokens.
| Setting | Description |
|---|---|
| Container Id | Required. The Name of the Dynamic Container to add the field to (not its numeric ID). The container can be anywhere on the form, including inside another container. If no field has this Name, or the field isn't a Dynamic Container, the form fails to load. |
| Name | Required. The new field's Name, used for its token. It must be unique across the whole form - if any field already has this Name, the form fails to load. |
| Label | Required. The label shown to the user. |
| Hint | A short description shown with the field. Not available for Content Block. |
| Initial Value | The value the field starts with. |
| Width, Offset, Row, Row Span | The field's position in the container's grid, in columns and rows. Used only when the container's Layout Mode is Manual. Width defaults to 12. |
| Active, Active Condition | Active, Inactive, or Conditional. Inactive fields aren't rendered, submitted, or validated - see Active. |
| Visible On Load, Visible On Load Condition, React to Field Changes, Reactive Visibility Rule, Remove From Layout When Not Visible, Submit Value When Not Visible | The same options as the field's Visibility settings. |
| Enabled On Load, Enabled On Load Condition, Enable Dynamically, Enable Dynamically Condition, Submit Value When Disabled | The same options as the field's Enabled settings. Not available for Content Block. |
| Validation | A Validation Condition and a list of validators, the same as the field's Field Validators. Not available for Content Block. |
The Validation section is stored exactly as you configure it - tokens in it aren't replaced when the action runs. Every field added by the same action gets the same validators. To make only some fields required, use two Add ... Field actions, one with a Required validator and one without, and use each action's Condition to pick which one runs.
Like every action, each Add ... Field action also has a Condition (see Common Parameters). When the condition is false the field isn't added, which makes the Condition a simple way to add a field only for certain users or records.
Adding fields from SQL
Most dynamic forms get their field list from a database table. The pattern is:
- Create List from SQL loads one row per field, in the order they should appear.
- Execute Actions for each List Entry loops over the rows.
- Inside the loop, Add ... Field actions build each field from the row's columns, using tokens like
[QuestionsList:QuestionText]. When rows can produce different field types, use one Add action per type, each with a Condition on the row's type column.
Build each field's Name from a column that's unique for every row, such as a key or ID, so no two rows produce the same Name.
The SQL is only one source. Any action that produces a list works the same way, for example Create List from JSON after a Server Request to an external API.
Example: event registration with custom questions
Each event needs its own extra questions - one asks for dietary needs and a T-shirt size, another asks for a company name and arrival date. The questions are kept in a table, and one registration form serves every event. The event comes from the URL, for example /register?EventId=12.
The questions table:
CREATE TABLE EventQuestions (
QuestionId int IDENTITY PRIMARY KEY,
EventId int NOT NULL,
QuestionKey nvarchar(50) NOT NULL, -- e.g. DietaryNeeds; unique per event
QuestionText nvarchar(200) NOT NULL, -- the label shown to the user
QuestionType nvarchar(20) NOT NULL, -- Text, LongText or Date
SortOrder int NOT NULL
);
1. Build the form
On the Build tab, add:
- Text Boxes named
FullNameandEmail, for the questions every event asks. - A Dynamic Container named
QuestionsContainer, with Layout Mode set toVertical. - A Button named
Register.
2. Add the questions in On Preinit
On the Settings tab, under On Preinit, add these actions:
-
Create List from SQL
- SQL Query:
SELECT QuestionKey, QuestionText, QuestionType FROM EventQuestions WHERE EventId = @EventId ORDER BY SortOrder - Bind Tokens: Parameter Name
EventId, Parameter Value[QueryString:EventId] - List Name:
QuestionsList
- SQL Query:
-
Execute Actions for each List Entry, with List Name
QuestionsList. Inside its Action List, add:- Add Text Box Field, with Condition
[QuestionsList:QuestionType] == "Text" - Add Textarea Field, with Condition
[QuestionsList:QuestionType] == "LongText" - Add Date Field, with Condition
[QuestionsList:QuestionType] == "Date"
Set these three actions up the same way:
Setting Value Container Id QuestionsContainerName Q_[QuestionsList:QuestionKey]Label [QuestionsList:QuestionText] - Add Text Box Field, with Condition
Each row adds one field, in SortOrder, so event 12's form might get Q_DietaryNeeds (a Textarea) and Q_TShirtSize (a Text Box). If an event has no rows, the container stays empty and the form shows only the static fields.
3. Save the answers
The button's On Click Handler doesn't know in advance which questions an event has. So instead of using each field's own token, it saves all the answers at once with the container's :Json token. Add a Run SQL Query action:
- SQL Query:
INSERT INTO EventRegistrations (EventId, FullName, Email, Answers) VALUES (@EventId, @FullName, @Email, @Answers) - Bind Tokens:
EventId=[QueryString:EventId],FullName=[FullName],Email=[Email],Answers=[QuestionsContainer:Json]
Answers then holds something like {"Q_DietaryNeeds":"Vegetarian","Q_TShirtSize":"L"}, which SQL Server's OPENJSON can split back into rows for reporting.
The QuestionsList list from On Preinit isn't available at submit. If the click handler needs it, or any other token created during loading, keep it with Persist data for submit on the On Preinit panel (see Settings). The field values are always available.
Using the submitted values
At submit, a dynamic field works like any other field:
| Token | Description |
|---|---|
[FieldName] | Each added field's value, using the Name set in its action - for example [Q_DietaryNeeds]. |
[ContainerName:Json] | A JSON object of every field in the container, keyed by field Name - for example {"Q_DietaryNeeds":"Vegetarian","Q_TShirtSize":"L"}. |
[ContainerName:QueryString] | The same values in query-string format - for example Q_DietaryNeeds=Vegetarian&Q_TShirtSize=L. |
Use the field tokens when the field Names are fixed. Use the container tokens when the Names come from data and the click handler can't know them in advance. Fields in the container that aren't active, or that aren't submitted because they're hidden or disabled, are left out.
Things to keep in mind
- Only On Preinit. Fields can't be added after the form has loaded. To react to the user's choices, add every field that might be needed in On Preinit and use reactive visibility to show only the relevant ones, or use a Repeater.
- Names must be unique across the form, not just within the container. A duplicate Name - including one that matches a field placed in the Form Builder - stops the form from loading. Prefix generated Names, as with
Q_above, so they can't collide with your static fields. - Container Id is the container's Name. It's the same Name you'd use in a token, not a numeric ID.
- Order follows the actions. Added fields go after any fields placed in the container at design time, in the order the actions ran. In a loop, sort the rows in the query. In
Manuallayout, set Row and Width explicitly. - Errors stop the form loading. A missing container, a duplicate Name, or a validator that doesn't exist makes the form fail to load. Test with data that covers every field type the actions can produce.
Related
- Dynamic Container - the container that receives dynamic fields
- Repeater - user-driven repeating groups of fields
- Common Field Settings - visibility, enabled, active, and validator settings
- Settings - the form's On Preinit and other events
- Create List from SQL and Execute Actions for each List Entry