简体   繁体   中英

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.

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());

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

You don't really need to add the event listener to first_field or second_field themselves. You can keep the event listener on the parent form and check both the submitted and the set data for whether or not it includes data for first_field . If it does, add the second_field . In the same listener, check if data has been set or submitted to second_field . If it has, add the third field.

There is a similar concept outlined in the documentation here: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM