Boulboulouboule Boulboulouboule - 2 months ago 20
Javascript Question

Symfony - FormType - Dynamic choices

I've a Form with 1 EntityType field that must contain choices depending of a second EntityType field that is not mapped in the first entity, like this :

ServicePlaceType.php :

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('placetype', EntityType::class, array(
"class" => "AppBundle:PlaceType",
"choice_label" => "place",
"mapped" => false
))
->add('idplace', EntityType::class, array(
"class" => "AppBundle:Place",
"choice_label" => "place"
))
->add('...');


The tables

+---------+--------------+---------------+-----------+
| Service | ServicePlace | Place | PlaceType |
+---------+--------------+---------------+-----------+
| | id | | |
+---------+--------------+---------------+-----------+
| | idplace > | < id | |
+---------+--------------+---------------+-----------+
| id > | < idservice | idPlaceType > | < id |
+---------+--------------+---------------+-----------+
| service | | place | placetype |
+---------+--------------+---------------+-----------+


So, when i select a PlaceType i want that the Place select shows only the places where idplacetype match the PlaceType id.

I tried in javascript, with an onChange event on the PlaceType select, that filter the Place options according to the PlaceType actual value, but i don't know how to fetch the PlaceType property of the Place in the formType.
I tried that kind of things, but it doesn't work

->add('idplace', EntityType::class, array(
"class" => "AppBundle:Place",
"choice_label" => "place",
"attr" => array("placeType" => $this->getPlaceType()), // nor like that
))

->add('idplace', EntityType::class, array(
"class" => "AppBundle:Place",
"choice_label" => "place",
"attr" => array("placeType" => function ($place) {
return $place->getPlaceType();
}), // neither like that
))


Does someone know how to fetch these datas ? Or how to dynamically filter options by another way ?

Thanks for the help !

Answer

You can do it with jquery library a little more simpler:

First, we change the builder a bit for render the place type id into <option data-type="..."> using the choice_attr option:

$builder
    ->add('placetype', EntityType::class, array(
        "class" => "AppBundle:PlaceType",
        "mapped" => false
    ))
    ->add('idplace', EntityType::class, array(
        "class" => "AppBundle:Place",
        'choice_attr' => function ($place) {
            // output: <option data-type="...">...</option>
            return ['data-type' => $place->getPlaceType()->getId()];
        },
    ))

Next, in you template:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

{# ... #}

{{ form(form) }}

<script>
    // when the 1st <select> was changed, then update the 2nd 
    // from current value and data-type option attribute.
    $(document).on('change', '#form_placetype', function () {
        var $idplace = $('#form_idplace'),
            // current value
            placetype = $(this).val(),
            // select available options from current value
            $available = $idplace.find('option[data-type="' + placetype + '"]');

        // deselect when the 1st <select> has changed.
        $idplace.val('');
        // hide no available options from current value
        $idplace.find('option').not($available).hide();
        // show available options from current value
        $available.show();
    });

    // Update 2nd <select> on page load.
    $('#form_placetype').trigger('change');
</script>
Comments