Populate Field Options at Template Level

Sometimes you may need to populate field options such as Selects, Checkbox Groups or Radio Groups dynamically at 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 workaround 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 Composer in order to work with field types like Select, Checkbox groups, etc. To workaround 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.

Here's how to make those changes on your site:

  1. Create a new form or edit an existing form as usual. Add a new Text field - let's call it Services with a handle of services, and place it somewhere into your form layout.
  2. 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 }}
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
    • 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 }}
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
    • 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 }}
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
  3. 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.
Last Updated: 6/26/2020, 12:39:49 PM