Symfony 6 : show conditional form field depending on first field choice

144 views Asked by At

I want to show or hide the "presenceDejeuner" and "participerActivite" fields based on the selection of "presenceEvent" (set to "Oui" or "true"). Symfony waits for form validation to recognize field choices, but I need to conditionally display these fields. I've tried using an event listener, but it only works when fields are pre-filled. However, in my case, they're not. How can I achieve this, and how can I dynamically change the "required" attribute for these fields using JavaScript? I've searched extensively but haven't found a solution. Any suggestions?

public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('presenceEvent', ChoiceType::class, [
                'choices' => [
                    'Oui' => true,
                    'Non' => false,
                ],
                'expanded' => false,
                'multiple' => false,
            ])
            ->add('presenceDejeuner', ChoiceType::class, [
                'choices' => [
                    'Oui' => true,
                    'Non' => false,
                ],
                'expanded' => false,
                'multiple' => false,
                'required' => false
            ])
            ->add('participerActivite', EntityType::class, [
                'mapped' => false,
                'class' => Activite::class,
                'choice_label' => 'activite',
                'multiple' => true,
                'expanded' => true,
                'required' => false
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Inscription::class,
        ]);
    }

enter image description here

I tryed everything what says in symfony docs such as PRE_SET_DATA , PRE_SUBMIT , Form Events etc.. Tryed also with Vanilla JS, but it just cannot recognize the choice of first field because it doesn't change dynamically in HTML

EDIT / Problem SOLVED : in Javascript, I have to find the id of the field to show or hide that was generated by symfony, I saw it in the dev tools and it was a bit strange I didn't expect that.

1

There are 1 answers

0
David On
  1. Because the “presenceDejeuner” and “participerActivite” fields (call them “Bar”) are conditionally connected to the “presenceEvent” field (call it “Foo”), the UI needs to change before the page is reloaded. This means Javascript is required (as you correctly guessed).
  2. Since Javascript is involved, the PHP app should always render Bar in the HTML, so that the HTML stays consistent and the JS doesn’t have to create the input from scratch.
  3. Because the visibility of Bar changes based on Foo, you’ll need to listen to Foo for change or input events. See below.
  4. Because Foo’s value is pre-rendered by PHP, you’ll need to check Foo’s value with JS after it loads without waiting for the event above. See below.

Change the UI

function get_inputs() { /* for convenience */
    return document.querySelectorAll(
        '#id_of_presenceDejeuner_input, #id_of_participerActivite_input'
    );
}

function enable_inputs() {
    for (const input of get_inputs()) {
        input.removeAttribute('disabled');
        input.setAttribute('required', '');
    }
}

function disable_inputs() {
    for (const input of get_inputs()) {
        input.setAttribute('disabled', '');
        input.removeAttribute('required');
    }
}

Check conditions

function test_conditions() {
    let input = document.querySelector('#id_of_presenceEvent_input');

    if (input.checked /* if rendered as checkbox */) {
        enable_inputs();
    } else {
        disable_inputs();
    }
}

Listen for events

document.addEventListener('DOMContentLoaded', ()=> {
    test_conditions();

    for (const input of get_inputs()) {
        input.addEventListener('change', test_conditions);
    }
});