Enable Select2Search in Symfony form
Asked Answered
L

4

5

I want to enable select 2 search in my Symfony form what i tried so far:

In my form class i have this:

->add('parent', EntityType::class, [
                'class' => Category::class,
                'choice_label' => 'title',
                'attr' => [
                    'class' => 'select2'
                ]
            ])

In my twig file this :

<head>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2-rc.1/css/select2.min.css" rel="stylesheet" />
    <!-- Loading jquery here--><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2-rc.1/js/select2.min.js"></script>
</head>

    {{ form_start(form) }}
    <script type="text/javascript">
        $('select').select2();
    </script>
    {{ form_widget(form) }}
    {{ form_end(form) }}

But i do not get the dropdown with the search bar. Just the default dropdown menu of Symfony. What am I doing wrong

Lunsford answered 18/5, 2018 at 21:32 Comment(7)
Is this the exact content of your template or did you just post the relevant parts?Failure
@Failure exact content of the twig fileLunsford
I've updated my answer with a code sample.Failure
have you solved the problem already?Crinose
@JuanI.MoralesPestana Nope, getting some errors if i load multiple jQueryLunsford
@Lunsford please update your question with all the options and errors, so I can se the errors help you. For me its working perfectly. Please update your question with the Entity, the form, datatransformer if you have and the view, also add the errorsCrinose
have you tested my last answer? is the same for multiple coices. you only have to set the option multiple true in form and code the provide the proper mapping informationCrinose
F
10

The main reason is that the field is created after you try and target it, by this line:

{{ form_widget(form) }}

The JavaScript must be executed after that in order to be able to target the field (besides, the HTML structure of your template is wrong).

Try this :

<!DOCTYPE html>
<html>
<head>
    <title>Test form</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2-rc.1/css/select2.min.css" rel="stylesheet" />
    <!-- Loading jquery here--><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2-rc.1/js/select2.min.js"></script>
</head>
<body>
    {{ form_start(form) }}
    {{ form_widget(form) }}
    {{ form_end(form) }}

    <script>
        $('select').select2();
    </script>
</body>
</html>

It's usually better to wait for the page to be loaded before executing scripts, using jQuery you could ensure that it's the case by changing the script to this:

<script>
    $(document).ready(function(){
        $('.select2').select2();
    });
</script>

Notice that I also changed the jQuery selector to use the class you've added to the field in your form builder. This way you control the select field you want to target.

Failure answered 19/5, 2018 at 1:56 Comment(0)
C
4

You are initiating the select2 components without configuration, so it doesn't know where is the data source.

Before start coding, you need to install and configure FOSJsRoutingBundle. This bundle will help you with access to ajax routes

For fully configured sync symfony-forms~select2 you could do something like this.

Entity Person

class Person
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="person_id_seq", allocationSize=1, initialValue=1)
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", nullable=true)
     */
    private $name;

    /**
     * @var Country
     *
     * @ORM\ManyToOne(targetEntity="Country")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="country_id", referencedColumnName="id")
     * })
     */
    private $country;



    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return Person
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set country
     *
     * @param \AppBundle\Entity\Country $country
     *
     * @return Person
     */
    public function setCountry(\AppBundle\Entity\Country $country = null)
    {
        $this->country = $country;

        return $this;
    }

    /**
     * Get country
     *
     * @return \AppBundle\Entity\Country
     */
    public function getCountry()
    {
        return $this->country;
    }

    public function __toString()
    {
        return $this->name;
    }
}

Entity Country

class Country
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="country_id_seq", allocationSize=1, initialValue=1)
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", nullable=true)
     */
    private $name;

    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return Country
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    public function __toString()
    {
        return $this->name;
    }
}

Country repository

class CountryRepository extends \Doctrine\ORM\EntityRepository {

 public function countriesSelect2($term)
    {
        $qb = $this->createQueryBuilder('c');

        $qb->where(
            $qb->expr()->like($qb->expr()->lower('c.name'), ':term')
        )
            ->setParameter('term', '%' . strtolower($term) . '%');

        return $qb->getQuery()->getArrayResult();
    }
}

Country controller

Check how the route is exposed to the options parameter and returns a JsonResponse. You could also use a serializer too.

/**
 * Country controller.
 *
 * @Route("countries")
 */
class CountryController extends Controller
{
    /**
     * Lists all person entities.
     *
     * @Route("/", name="countries",options={"expose"=true})
     * @Method("GET")
     */
    public function indexAction(Request $request)
    {
        $countryRepo = $this->getDoctrine()->getRepository('AppBundle:Country');
        $data = $countryRepo->countriesSelect2($request->get('q', ''));

        //$response = $this->get('serializer')->serialize($data,'json');

        return new JsonResponse($data);
    }
}

So far so good, now comes the good parts, let's go and configure our form

PersonType

class PersonType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name')
            ->add('country',EntityType::class,[
                'class' => Country::class,
                'attr' => [
                  'class' => 'select2', // the class to use with jquery
                    'data-source' => 'countries', //the exposed route name for data-soirce as attr
                    'data-allow-clear' => 'true'//another extra attr to customize
                ],
            ]);
    }/**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Person'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_person';
    }
}

JS, showing the select2

Remember, you have configured already the select2 options with attributes, you just have to use them properly

$(document).ready(function () {
            $('.select2').each(function () {//using the select2 class
                if (!$().select2) {//checking the script
                    return;
                }
                $.fn.select2.defaults.set("theme", "bootstrap");//some theming if you want

                $($(this)).select2({
                    placeholder: "Select",
                    width: 'auto',
                    allowClear: $(this).attr("data-allow-clear") ? $(this).attr("data-allow-clear") : true, //using my options from the form
                    ajax: {
                        url: Routing.generate($(this).attr("data-source")), //here its the magic
                        dataType: 'json',
                        processResults: function (data) {
                            //console.log(data);
                            return {
                                results: $.map(data, function (item) {
                                    return {
                                        text: item.name, //you need to map this because the plugin accepts only id and text
                                        id: item.id
                                    }
                                })
                            };
                        }
                    }
                });
            });
        });

after that, all is done. All the code is working as I tested my self

Hope it helps!

Crinose answered 25/5, 2018 at 18:22 Comment(0)
M
2

There is a nice bundle for it: TetranzBundle

You can configure your form field in FormType class like that:

            ->add('product', Select2EntityType::class, [
                'label'=>'product',
                'required'=>true,
                'mapped'=>true,
                'multiple' => false,
                'remote_route' => 'product_select2_ajax',
                'class' => 'AppBundle:Product',
//                'property' => 'name',
                'minimum_input_length' => 0,
                'page_limit' => 10,
                'allow_clear' => true,
                'delay' => 250,
                'cache' => true,
                'cache_timeout' => 60000, // if 'cache' is true
                'language' => 'pl',
                'placeholder' => "select.product",
            ])
Maniple answered 19/5, 2018 at 12:28 Comment(0)
F
1

Try this :

<script type="text/javascript">
    $(document).ready(function() {
    $('.select2').select2(); //instead $('select2').select2();
});
</script>

see How to select a class in Jquery and the basic usage exemple

Frameup answered 28/5, 2018 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.