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

Show a field only when another field has a given value

One rule that both shows the field and makes it required, with Required left unticked, plus the conditionals printed for the browser. Required with a show rule alone is the form nobody can submit.

Context

Context. A drinks order asks Choose your drink, and only someone who chooses tea should be asked Which tea?, which must then be answered. It is the most common conditional on any form, and the most common way to build a form that cannot be submitted: tick Required on the tea field, add a rule that shows it, and every coffee order is refused by the server with an error under a field nobody can see. This is the configuration both sides agree on.

Steps

  1. Add a select drinkType with option values including tea, and a select teaType. Leave Required unticked on teaType.
  2. Add one conditional: String comparison, drinkType Is equal to tea, with two actions: Field visibility › Show teaType, and Field requirements › Set required on teaType.
  3. Wrap the form in a .coredna-js-form container, load js-forms-conditionals.js with the page's nonce, and print the conditionals (below). The container is needed on a page-per-step form too: the conditionals script works only inside it.
  4. Keep both fields on the same step. On a page-per-step form, a condition sees only the answers of the step being submitted.
  5. Test three cases on a development site: coffee (the tea field never appears, and the order completes), tea with no tea type (stopped, with the message under the tea field), and tea with a tea type (completes).

Code

Template code
{$rules = []}
{foreach $form->getActiveRevision()->getConditionals() as $conditional}
  {$rules[] = ['conditions' => $conditional->getConditions(), 'actions' => $conditional->getActions()]}
{/foreach}
<div class="coredna-js-form">
  {* the form itself, as in the form layout *}
</div>
<script nonce="{$_inlineScriptNonce}">
  window['formConditionals{$form->getId()}'] = {$rules|json_encode};
</script>
<script src="/assets/forms/js-forms-conditionals.js" nonce="{$_inlineScriptNonce}" defer></script>
{% set rules = [] %}
{% for conditional in form.getActiveRevision().getConditionals() %}
  {% set rules = rules|merge([{ conditions: conditional.getConditions(), actions: conditional.getActions() }]) %}
{% endfor %}
<div class="coredna-js-form">
  {# the form itself, as in the form layout #}
</div>
<script nonce="{{ _inlineScriptNonce }}">
  window['formConditionals{{ form.getId() }}'] = {{ rules|json_encode }};
</script>
<script src="/assets/forms/js-forms-conditionals.js" nonce="{{ _inlineScriptNonce }}" defer></script>

Without the formConditionals variable the script logs "Conditionals not found for form ID" and every field stays visible.

Why this and not that

  • Not "Required plus a show rule". The browser starts a field that a rule shows as hidden; the server starts it as visible, and hides it only when a matching rule says so. With Required ticked, a coffee order has the tea field hidden and empty in the browser and visible and required on the server, so it is refused. Required unticked plus Set required in the same rule is one rule, and it is required exactly when it is shown, on both sides.
  • Compare the stored value. A select stores the option's value (tea), not its title (Tea), and so does a radio list. The condition, any job and any export all see the value.
  • The alternatives. Two other configurations also work: Required plus an explicit hide rule for the opposite case, or Required plus an unconditional hide rule placed first. Both need two rules kept in step forever. They are set out on Conditional logic.
Go deeper

Was this page helpful?

Your answer goes to the docs team.