123pierre
123pierre

Reputation: 305

Symfony 2.0 updating select options with JS?

I've been googling for hours but surprisingly I didn't find any topic on that subject.

I have the following Form

class propertyType extends AbstractType
{

    public function buildForm(FormBuilder $builder, array $options)
    {

        $builder
            ->add('city')
            ->add('district', 'entity', array('class'=>'FlatShanghaidefaultBundle:district', 
                                   'property'=>'name',
                                   'query_builder' => function ($repository) {
                                   $qb = $repository->createQueryBuilder('district');
                                   $qb->add('where', 'city = :city');
                                   $qb->setParameter('city', 1);
                                   return $qb;

    }


    public function getName()
    {
        return 'property';
    }
}

When the user choose a City in the form, I want the options of district to be dynamically updated and limited to that city. With Ajax or JS? What would be the best practice? Do you know a tutorial on that topic? If someone can put me on the right tracks, that would help a lot..

Thanks!

Upvotes: 3

Views: 4045

Answers (3)

Sébastien
Sébastien

Reputation: 5453

I'm doing this myself on a form. I change a field (a product) and the units in which the quantity can be measured are updated. I am using a macro with parameters to adapt it more easily.

The macro :

{% macro javascript_filter_unit(event, selector) %}
<script>
    $(function(){
        $('#usersection')
                .on('{{ event }}', '{{ selector }}', function(e){
                    e.preventDefault();
                    if (!$(this).val()) return;
                    $.ajax({
                        $parent: $(this).closest('.child_collection'),
                        url: $(this).attr('data-url'),
                        type: "get",
                        dataType: "json",
                        data: {'id' : $(this).val(), 'repo': $(this).attr('data-repo'), parameter: $(this).attr('data-parameter')},
                        success: function (result) {
                            if (result['success'])
                            {
                                var units = result['units'];
                                this.$parent.find('.unit').eq(0).html(units);   
                            }
                        }
                    });
                })
    });

</script>

{% endmacro %}

The ajax returns an array : array('success' => $value, 'units' => $html). You use the $html code and put it in place of the select you want to change. Of course the javascript code of the ajax call need to be modfied to match your fields.

You call the macro like you would normally do:

{% import ':Model/Macros:_macros.html.twig' as macros %}
{{ macros.javascript_filter_unit('change', '.unitTrigger') }}

So I have two arguments : the event, often a change of a select. and a selector, the one whose change triggers the ajax call.

I hope that helps.

Upvotes: 0

room13
room13

Reputation: 1922

Jbm is right about the query builder. And his approach is perfecly valid.

Another option could be to dispense the cascade select in favor of an autocomplete field.

Assuming that you save the countries, cities and districts as entities and have a relation between them, you do not even need to save what city/country has been selected because you can just call:

$district->getCity()->getCountry();

I have implemented a similar thing for country/city selection and will link here to the the main involved files.

First, create a custom form type to encapsulate all form stuff, it contains a hidden field to store the selected id and a text field to serve as input for the autocomplete logic:

https://github.com/roomthirteen/Room13GeoBundle/blob/master/Form/LocationFieldType.php

Then theme the form type:

https://github.com/roomthirteen/Room13GeoBundle/blob/master/Resources/views/Form/fields.html.twig

The url of the autocomplete source is passed as data attribute so no JS will be smutching the html code.

Last but not least, the JS functions have to be implemented:

https://github.com/roomthirteen/Room13GeoBundle/blob/master/Resources/public/jquery.ui.location-autocomplete.js

The result can be seen in the image below, see that for clarity the country name will be displayed in braces behind the city name:

autocomplete is loading

--

I favor this solution much more that using cascade selects because the actual value can be selected in one step.

cheers autocomplete has loaded and city has to be selected

Upvotes: 1

Jens
Jens

Reputation: 5877

The query builder will not solve your problem, you can remove it altogether.

That query is run when the form gets built, once you have it on your browser you need to use javascript to populate the options.

You can have the options stored in a javascript variable, or pull them from the server as needed with ajax (you will need a controller to handle these ajax requests).

You will probably want to use some jquery plugin to handle the cascading logic between the select elements, there are a couple available:

There is also at least this Bundle I know of: https://github.com/genemu/GenemuFormBundle, which has ajax field types available for several jquery plugins. This may save you writing the ajax part to handle the data, as it comes built in (it's probably easier to implement the controller your self anyway). I haven't tried this one, and I don't know if it has cascading support.

Upvotes: 2

Related Questions