How to add an Event Listener to a dynamically added field using Symfony Forms

11,597

Solution 1

If, is it actually.

FormInterface does't have the addEventListener method, but FormBuilderIntreface have it. If you want to add any listener, you should to create form field by form builder.

For example:

   // create builder for field
   $builder = $form->getConfig()->getFormFactory()->createNamedBuilder($name, $type, null, array(
       /** any options **/
       'auto_initialize'=>false // it's important!!!
   ));
   // now you can add listener
   $builder->addEventListener(FormEvents::POST_SUBMIT, $yourCallbackHere)

   // and only now you can add field to form  
   $form->add($builder->getForm());

Solution 2

I have just spent half of my working day struggling on this. I'm using symfony 3.2.x and the best thing that helped me is this answer.

I had a triple dependece (country,state,region,zipcode) i've solved all inside the same FormType:

    /** @var  EntityManager $em */
private $em;

/**
 * GaraFormType constructor.
 *
 * @param EntityManager $em
 */
public function __construct(EntityManager $em)
{
    $this->em        = $em;
}


    public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('country', EntityType::class, array(
            'class'       => 'Country',
            ...more attrs
        ));

    $this->stateAjaxFieldFormListener($builder);
    $this->cityAjaxFieldFormListener($builder);
    $this->zipCodeAjaxFieldFormListener($builder);
}

Each of those functions handles one of the dynamic fields, and they are all the same as follows:

private function stateAjaxFieldFormListener(FormBuilderInterface $builder)
{
    $localizationFormModifier = function (FormInterface $form, Country $country = null) {
        $stateInCountry = $this->em->getRepository("State")->findBy(array("country" => $country));

        if ($form->has('state') && ! $form->isSubmitted()) {
            $form->remove('state');
        }
        $form
            ->add('state', EntityType::class, array(
                'choices'     => $stateInCountry,
                'class'       => 'State',
            ));
    };
    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($localizationFormModifier) {
        /** @var ClienteTemp $data */
        $data    = $event->getData();
        $country = null !== $data ? $data->getCountry() : null;

        $localizationFormModifier($event->getForm(), $country);
    });
    $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($localizationFormModifier) {
        $data      = $event->getData();
        $countryId = array_key_exists('country', $data) ? $data['country'] : null;
        $country   = $this->em->getRepository("Country")->find($countryId);
        $localizationFormModifier($event->getForm(), $country);
    });
}

Just change entity references for the other two functions: cityAjaxFieldFormListener and zipCodeAjaxFieldFormListener

Share:
11,597
Chase
Author by

Chase

Development has been a passion of mine for a very long time! They say if you love what you do then you never work a day in your life. Id say thats mostly true. I have opinions on almost everything and strong opinions on some things. I really enjoying hearing opposing views and discussing and debating the merits of all sides!

Updated on June 05, 2022

Comments

  • Chase
    Chase almost 2 years

    I am using event listeners to dynamically modify a form. I want to add another event listener to a field that was added dynamically. Im not sure how to accomplish this.

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('first_field','choice',array(
            'choices'=>array('1'=>'First Choice','2'=>'Second Choice')
        ));
    
        $builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'preSetData'));
        $builder->get('first_field')->addEventListener(FormEvents::POST_SUBMIT, array($this, 'postSubmit'));
    }
    
    public function preSetData(FormEvent $event)
    {
        $form = $event->getForm();
        $form->add('second_field','choice',array(
            'choices'=>array('1'=>'First Choice','2'=>'Second Choice')
        ));
        //Some how add an event listener to this field
    
    }
    
    public function postSubmit(FormEvent $event)
    {
        $form = $event->getForm()->getParent();
        $form->add('second_field','choice',array(
            'choices'=>array('1'=>'First Choice','2'=>'Second Choice')
        ));
        //Some how add an event listener to this field
    }
    

    I have trie just using the $builder in the buildForm function to add the event listener to the second_field but because the field doesnt exist when the form is initially generated it throws an error.

    If i try and add the new event listener inside the first event listener by doing:

    $form->get('second_field')->addEventListener(...)
    

    Then i get the error:

    Call to undefined method Symfony\Component\Form\Form::addEventListener() 
    

    Any suggestions would be welcome.