-->

How to add an Event Listener to a dynamically adde

2019-05-23 07:33发布

问题:

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.

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


回答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



回答3:

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