symfony3 How to extend ChoiceType, EntityType and DocumentType to load choices with AJAX.


Example

In Symfony, the built-in ChoiceType (and EntityType or DocumentType extending it), basicaly work with a constant choice list.

If you want to make it work with ajax calls, you have to change them to accept any sumitted extra choices.

  • How to start with an empty choice list ?

    When you build your form, just set the choices option to an empty array() :

     namespace AppBundle\Form;
    
     use Symfony\Component\Form\AbstractType;
    
     use Symfony\Component\Form\FormBuilderInterface;
    
     use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
    
     class FooType extends AbstractType
     {
         public function buildForm(FormBuilderInterface $builder, array $options)
         {
             $builder
                 ->add('tag', ChoiceType::class, array('choices'=>array()));
         }
     }
    

    So you will get an empty select input, without choices. This solution works for ChoiceType and all of his children (EntityType, DocumentType, ...).

  • How to accept submitted new choices :

    To accept the new choices, you have to make them available in the form field choicelist. You can change your form field depending on submitted data with the FormEvent::PRE_SUBMIT event.

    This example show how to do it with a basic ChoiceType :

     namespace AppBundle\Form;
    
     use Symfony\Component\Form\AbstractType;
    
     use Symfony\Component\Form\FormBuilderInterface;
    
     use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
    
     class FooType extends AbstractType
     {
         public function buildForm(FormBuilderInterface $builder, array $options)
         {
             $builder
                 ->add('tag', ChoiceType::class, array('choices'=>array()))
             ;
             
             $builder->addEventListener(
                 FormEvents::PRE_SUBMIT,
                 function(FormEvent $event){
                     // Get the parent form
                     $form = $event->getForm();
                     
                     // Get the data for the choice field
                     $data = $event->getData()['tag'];
                     
                     // Collect the new choices
                     $choices = array();
                     
                     if(is_array($data)){
                         foreach($data as $choice){
                             $choices[$choice] = $choice;
                         }
                     }
                     else{
                         $choices[$data] = $data;
                     }
                     
                     // Add the field again, with the new choices :
                     $form->add('tag', ChoiceType::class, array('choices'=>$choices));
                 }
             );
         }
     }
    

    Your submitted choices are now allowed choices and Symfony ChoiceType built-in validation won't reject them anymore.

    If you wan to do the same with a ChoiceType child (EntityType, DocumentType, ...), you have to inject the entityManager or the documentManager and to do the datatransformation when populating the new choices.