How to Show Dependant Fields for Checkboxes using Symfony 3

Logo of symfony on black background

We are currently working on a project that we are building using Symfony 3, and some days ago we faced the challenge of showing a list of checkboxes but considering that some of them could have sub-options when selected. In short, something like this:

For that purpose, we created a form with the fields we needed:

...
class CandidateInfoType extends BaseWizardStepType
{
    ...
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);
	    $builder
		    ->add('areasOfExpertise', null, [
			    'expanded' => true,
			    'multiple' => true,
		    ])
		    ->add('areaExpertiseEspOtherDesc', TextType::class, [
			    'label' => 'Please describe the topic',
			    'required' => false,
		    ])
		    ->add('areaExpertiseOtherDesc', TextType::class, [
			    'label' => 'Please describe the topic',
			    'required' => false,
		    ]);
...

Of course, there are a bunch of other fields missing there, but we are focusing on what matters here 😉

  • areasOfExpertise generates the list and their values come from an associated Entity, among those options we have these two: “ESP – other” and “Other”.
  • areaExperiseEspOtherDesc is the field to keep a custom value but only when the user selects “ESP – other” from the list.
  • areaExpertiseOtherDesc works similar to areaExperiseEspOtherDesc but when the user selects “Other” from the list.

In regards to our structure, we have two entities associated via a ManyToMany relationship which is modeled this way:

A Project Entity:

...
/**
 * Project entity.
 * @ORM\Entity()
 */
class Project
{
    /**
     * @var ArrayCollection $areasOfExpertise
     *
     * @ORM\ManyToMany(targetEntity="AreaOfExpertise")
     * @ORM\JoinTable(name="projects_areasofexpertise")
     */
    protected $areasOfExpertise;
    /**
     * @var string
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $areaExpertiseEspOtherDesc;
    /**
     * @var string
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $areaExpertiseOtherDesc;
...

And the AreaOfExpertise entity (what’s shown below is a simplified code), in this case we don’t really need to define the inverse association and that’s why you won’t see the ManyToMany association in the following code:

...
/**
 * AreaOfExpertise entity.
 * @ORM\Entity()
 */
class AreaOfExpertise
{
...
    /**
     * @var string $name
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    protected $name;
...

So far, there’s nothing extraordinary, the main trick is the override that we made to the form widget block in the Twig template that is in charge of generating the HTML, to make it simple, let’s do it directly in the template file:

{% form_theme form _self %}

{%- block _project_areasOfExpertise_widget -%}

<div {{ block('widget_container_attributes') }}>

<div class="row">

<div class="col-sm-6">
		{%- for child in form %}
			{{- form_widget(child) -}}
			{{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
			{% if child.vars.label == 'ESP - other' %}
				<div{% if not child.vars.checked %} class="hidden"{% endif %}>
				{{ form_label(form.parent.areaExpertiseEspOtherDesc) }}
				{{ form_widget(form.parent.areaExpertiseEspOtherDesc) }}
				</div>

			{% elseif child.vars.label == 'Other' %}
				<div{% if not child.vars.checked %} class="hidden"{% endif %}>
				{{ form_label(form.parent.areaExpertiseOtherDesc) }}
				{{ form_widget(form.parent.areaExpertiseOtherDesc) }}
				</div>

			{% endif %}

			{% if loop.index == (loop.length / 2)|round(0, 'ceil')  %}
		</div>


<div class="col-sm-6">
		{% endif %}
	{% endfor -%}
		</div>

	</div>

</div>

{%- endblock _project_areasOfExpertise_widget -%}

  • The first line of code instructs Twig to use any fragment defined in the current template(_self) to render form widgets
  • Then, we defined the block _project_areasOfExpertise_widget, which takes precedence over the default Twig widget to generate checkbox lists, more information about the Twig theming in Symfony can be found at How to Work with Form Themes
  • Notice that we also did some tweaks in the loop to generate the two column of options
  • We also added some Javascript code in order to hide or show the “Other” text fields, something that we could discuss in a near future.

So there you have it! Now you can override any list of options in Symfony to show dependant fields when they’re selected.

Tagged under: