How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

How-to Forms

Build a multi-step form with conditional steps

A step shown only for business enquiries: the JS model, a group around the step's fields, and one hide rule acting on both the step and the group, because hiding a step alone leaves its required fields required on the server.

Context

Context. An enquiry form asks whether the enquiry is personal or for a business, and only business enquiries see a second step of company questions, some of them required. Skipping a step is easy to configure and easy to get wrong: hiding a step does not make its required fields optional on the server, so a configuration that looks right can leave personal enquiries unable to submit. This is the configuration that works on both sides.

Steps

  1. Use the JS submission model (every step in one page, sent together at the end). The rule on step 2 depends on an answer from step 1, and only the JS model gives the server every step's answers at once (see Conditional logic).
  2. Build the steps: About you with a required select enquiryType (values personal and business), Company details, Your enquiry, then the result step.
  3. On Company details, put every field inside one group, Company. Tick Required on the fields that must be answered for business enquiries.
  4. Add one conditional: String comparison, enquiryType Is not equal to business. Give it two actions: Step visibility › Hide Company details, and Group visibility › Hide Company.
  5. Print the conditionals for the browser in the form layout (below), with the JS form markup from Multi-step forms.
  6. Test both paths on a development site: a personal enquiry must go from step 1 straight to Your enquiry and complete; a business enquiry must stop on an empty required company field.

Code

The conditionals, printed once in the form layout so the browser can apply them:

Template code
{$rules = []}
{foreach $form->getActiveRevision()->getConditionals() as $conditional}
  {$rules[] = ['conditions' => $conditional->getConditions(), 'actions' => $conditional->getActions()]}
{/foreach}
<script nonce="{$_inlineScriptNonce}">
  window['formConditionals{$form->getId()}'] = {$rules|json_encode};
</script>
{% set rules = [] %}
{% for conditional in form.getActiveRevision().getConditionals() %}
  {% set rules = rules|merge([{ conditions: conditional.getConditions(), actions: conditional.getActions() }]) %}
{% endfor %}
<script nonce="{{ _inlineScriptNonce }}">
  window['formConditionals{{ form.getId() }}'] = {{ rules|json_encode }};
</script>

Each step's .form-step must carry data-step-name with the step's exact name, which is how the step rule finds it, and the page must load js-forms.js and js-forms-conditionals.js (with the page's nonce).

Why this and not that

  • Why the group as well as the step. The step rule hides the step in the browser and makes navigation skip it. On the server it does nothing: the platform looks a hidden step's fields up by name and compares them by id, so they never match, and the step's required fields stay required. The group rule is the one the server honours: a hidden group's fields are not required. Each rule covers the side the other misses.
  • Why "is not equal to business" and a hide, rather than "equals business" and a show. The browser starts a field or group that some rule shows as hidden, but the server starts it as visible, so a show rule leaves the company fields required on the server for every personal enquiry. A hide rule starts visible on both sides and is hidden by both when it matches. Make enquiryType itself required, so the condition always has an answer to compare by the time the visitor leaves step 1.
  • Why not the page-per-step model. Each of its requests carries only one step's answers, so when step 2 is submitted the condition sees no enquiryType at all. In that model, keep each condition on the same step as the fields it affects.
Go deeper

Was this page helpful?

Your answer goes to the docs team.