Search code examples
htmlformssymfonydoctrine

Symfony Forms: HTML5 datalist


How can be implemented HTML5 datalist with values from the database (Doctrine)?

Purpose: replace selects with many options to inputs with autocompletion.


Solution

  • First, add your new FormType for the field:.

    <?php
    // src/Acme/Form/Type/DatalistType
    namespace Acme\Form\Type;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    use Symfony\Component\Form\FormInterface;
    use Symfony\Component\Form\FormView;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    
    class DatalistType extends AbstractType
    {
        public function getParent()
        {
            return TextType::class;
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setRequired(['choices']);
        }
    
        public function buildView(FormView $view, FormInterface $form, array $options)
        {
            $view->vars['choices'] = $options['choices'];
        }
    
        public function getName()
        {
            return 'datalist';
        }
    }
    

    In services.yml:

    form.type.datalist_type:
        class: Acme\Form\Type\DatalistType
        tags:
            -  { name: form.type, alias: datalist }
    

    Do you have a form theme? If yes, skip to the next step, if no, create a new one in app/Resources/views/Form/fields.html.twig and change your default Twig theme to it:

    # app/config/config.yml
    twig:
        form_themes:
            - ':Form:fields.html.twig'
    

    Now define a template for your new field in the form theme:

    {% block datalist_widget %}
        <input list="{{ id }}_list" {{ block('widget_attributes') }}{% if value is not empty %}value="{{ value }}"{% endif %}>
        <datalist id="{{ id }}_list">
            {% for choice in choices %}
                <option value="{{ choice }}"></option>
            {% endfor %}
        </datalist>
    {% endblock %}
    

    Use your field in FormType:

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('country', DatalistType::class, ['choices' => ['a', 'b']]); 
    }
    

    Instead of ['a', 'b'] You need to load your choices from DB somehow, I'd suggest passing them in form options as the easiest solution.


    Version notes:

    Symfony >= 3.0
    In class DatalistType change

    public function getName()
    

    to

    public function getBlockPrefix()
    

    Symfony >= 4.0
    You probably don't need the changes in services.yaml as they introduced auto registering of things, I believe as long as you extend AbstractType it'll work.