Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 875
Chase Avatar asked Aug 18 '14 00:08

Chase


2 Answers

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());
like image 163
Lev Semin Avatar answered Sep 20 '22 17:09

Lev Semin


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

like image 22
Diego Avatar answered Sep 22 '22 17:09

Diego