Populate Field Options at Template Level
Sometimes you may need to populate field options such as Dropdown, Checkboxes or Radios dynamically at the template level. Perhaps you're populating the options from a Craft Entry or some other Twig template code in your template. Freeform specifically wasn't designed to handle this, but you can work around it with this guide.
While Freeform does allow for populating field options with predefined data in the control panel, this isn't always enough for every use case. Further to this, Freeform explicitly requires that all options exist for the field in the form builder work with field types like Dropdown, Checkboxes, etc. To work around this, you can create the field as a regular Text field instead, and then modify the formatting template to account for it and include your own Twig code to pull in dynamic options. It isn't a perfect solution, but the closest you can get.
Instructions
Here's how to make those changes on your site:
Create a new form or edit an existing form as usual. Add a new Text field and let's call it Services with a handle of services
, and place it somewhere into your form layout.
Update your formatting template code...
-
Let's assume you're using something that resembles the Solspace sample Flexbox formatting template:
{{ form.renderTag }}
{% if form.pages|length > 1 %}
<ul class="freeform-pages">
{% for page in form.pages %}
<li>
{% if form.currentPage.index == page.index %}
<b>{{ page.label }}</b>
{% else %}
{{ page.label }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if form.hasErrors %}
<div class="freeform-form-has-errors">
{{ "Error! Please review the form and try submitting again."|t('freeform') }}
{% if form.errors|length %}
<ul>
{% for error in form.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endif %}
{% for row in form %}
<div class="freeform-row {{ form.customAttributes.rowClass }}">
{% for field in row %}
{% set columnClass = "freeform-column " ~ form.customAttributes.columnClass %}
{% if field.type == "submit" %}
{% set columnClass = columnClass ~ " freeform-column-content-align-" ~ field.position %}
{% endif %}
<div class="{{ columnClass }}"{{ field.rulesHtmlData }}>
{{ field.render({
class: field.type not in ["submit", "signature"] ? "freeform-input" : "",
labelClass: "freeform-label" ~ (field.inputOnly ? " freeform-input-only-label" : "") ~ (field.required ? " freeform-required" : ""),
errorClass: "freeform-errors",
instructionsClass: "freeform-instructions",
}) }}
</div>
{% endfor %}
</div>
{% endfor %}
{{ form.renderClosingTag }} -
Update it to include a conditional that checks on your new Services field:
{{ form.renderTag }}
... TRUNCATED FOR EXAMPLE ...
{% for row in form %}
<div class="freeform-row {{ form.customAttributes.rowClass }}">
{% for field in row %}
{% set columnClass = "freeform-column " ~ form.customAttributes.columnClass %}
{% if field.type == "submit" %}
{% set columnClass = columnClass ~ " freeform-column-content-align-" ~ field.position %}
{% endif %}
<div class="{{ columnClass }}"{{ field.rulesHtmlData }}>
{% if field.handle == "services" %}
// YOUR CUSTOM CODE HERE
{% else %}
{{ field.render({
class: field.type not in ["submit", "signature"] ? "freeform-input" : "",
labelClass: "freeform-label" ~ (field.inputOnly ? " freeform-input-only-label" : "") ~ (field.required ? " freeform-required" : ""),
errorClass: "freeform-errors",
instructionsClass: "freeform-instructions",
}) }}
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
{{ form.renderClosingTag }} -
Let's say you wanted to populate the Services field with options from a Craft Entry that is already loaded into the page, and you want to grab the options of that field (e.g.
companyServices
). The final code might look something like this:{{ form.renderTag }}
... TRUNCATED FOR EXAMPLE ...
{% for row in form %}
<div class="freeform-row {{ form.customAttributes.rowClass }}">
{% for field in row %}
{% set columnClass = "freeform-column " ~ form.customAttributes.columnClass %}
{% if field.type == "submit" %}
{% set columnClass = columnClass ~ " freeform-column-content-align-" ~ field.position %}
{% endif %}
<div class="{{ columnClass }}"{{ field.rulesHtmlData }}>
{% if field.handle == "services" %}
{{ field.renderLabel() }}
<select name="{{ field.handle }}" id="{{ field.idAttribute }}">
<option>Please select...</option>
{% for option in entry.companyServices %}
<option value="{{ option.value }}">{{ option.label }}</option>
{% endfor %}
</select>
{{ field.renderInstructions() }}
{{ field.renderErrors() }}
{% else %}
{{ field.render({
class: field.type not in ["submit", "signature"] ? "freeform-input" : "",
labelClass: "freeform-label" ~ (field.inputOnly ? " freeform-input-only-label" : "") ~ (field.required ? " freeform-required" : ""),
errorClass: "freeform-errors",
instructionsClass: "freeform-instructions",
}) }}
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
{{ form.renderClosingTag }}
Your form will now display options populated by the outside Craft Entry source, and allow you users to submit the form choosing one of those options. The data will then be stored in the Services text field as a simple string of text.