3

I have a location form with three "select" (choices list) : country, area and department. These "select" have by default all the list of country, area and department. I want to let my user choose a country first or an area or a department.

What I want to do:
When an user choose a country, datas in area change depending of it. Same between area and department.

What I have:

  • When I choose a country first then an area, datas auto-update works between country and area but with area to department, the AJAX request is not launched.
  • But if I choose directly an area then the AJAX request is launched but it does not update the data in department.
  • However, in the form, when I comment the part concerning country and area auto-update, the AJAX request is launched and the auto-update between area and department works.

Here my form :

//src/Form/LocationType.php
class LocationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('country', EntityType::class, [
        'class' => Country::class,
            ])
            ->add('area', EntityType::class, [
                'class' => Area::class,
                'required' => false
            ])
            ->add('department', EntityType::class, [
                'class' => Department::class,
                'required' => false
            ]);
            
        //The part I comment for makes the auto-update between area and department works
        $formArea = function(FormInterFace $form, Country $country = null) {
            if($country === null) {
                $form->add('area', EntityType::class, [
                    'class' => Area::class,
                    'required' => false
                ]);
            }
            else {
                $areas = $country->getAreas();
                $form->add('area', EntityType::class, [
                    'class' => Area::class,
                    'choices' => $areas,
                    'required' => false
                ]);
            }
        };

        $builder->get('country')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formArea){
                $country = $event->getForm()->getData();
                $formArea($event->getForm()->getParent(), $country);
            }
        );
        //

        $formDepartment = function(FormInterFace $form, Area $area = null) {
            if($area === null) {
                $form->add('department', EntityType::class, [
                    'class' => Department::class,
                    'required' => false
                ]);
            }
            else {
                $departments = $area->getDepartments();
                $form->add('department', EntityType::class, [
                    'class' => Department::class,
                    'choices' => $departments,
                    'required' => false
                ]);
            }
        };

        $builder->get('area')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formDepartment){
                $area = $event->getForm()->getData();
                $formDepartment($event->getForm()->getParent(), $area);
            }
        );
}

And JS code with AJAX call:

//assets/js/selectCountryAreaDepartment.js
window.onload = () => {
    let country = document.querySelector("#location_country");

    country.addEventListener("change", function() {
        let form = this.closest("form");
        let data = this.name + "=" + this.value;
        console.log(data);
        fetch(form.action, {
            method: form.getAttribute("method"),
            body: data,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded; charset:UTF-8"
            }
        })
        .then(response => response.text())
        .then(html => {
            let content = document.createElement("html");
            content.innerHTML = html;
            let area = content.querySelector("#location_area");
            document.querySelector("#location_area").replaceWith(area);
            console.log(area);

        })
        .catch(error => {
            console.log(error);
        })
    });

    let area = document.querySelector("#location_area");

    area.addEventListener("change", function() {
        let formArea = this.closest("form");
        let dataArea = this.name + "=" + this.value;
        //console.log(formArea);
        console.log(dataArea);
        fetch(formArea.action, {
            method: formArea.getAttribute("method"),
            body: dataArea,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded; charset:UTF-8"
            }
        })
        .then(response => response.text())
        .then(html => {
            let contentArea = document.createElement("html");
            contentArea.innerHTML = html;
            let department = contentArea.querySelector("#location_department");
            document.querySelector("#location_department").replaceWith(department);
            console.log(department);
        })
        .catch(error => {
            console.log(error);
        })
    });

}

This code is based on this French tutorial : https://www.youtube.com/watch?v=f7tdb30evUk

3
  • I do not know symfony at all but I wonder why there is not closing ]); in the else section ot the function declaration : $formDepartment = function(FormInterFace $form, Area $area = null). To be more precise, 'required' => false -> 'required' => false ]); Commented Mar 3, 2022 at 7:58
  • @Stef1611 Sorry, It was just a copy/paste mistake. Commented Mar 3, 2022 at 9:03
  • My else in $formDepartment is correctly closed in my code. I accidently erased it in my StackOverflow question. It's corrected now. Commented Mar 3, 2022 at 9:18

1 Answer 1

1

By removing add() in null case in formModifiers formArea and formDepartment it avoid to "reset" select when an other select is used :

//src/Form/LocationType.php
// …
        // formModifier
        $formArea = function(FormInterFace $form, LocationCountry $country = null) {
            if($country != null)
            {
                $areas = $country->getAreas();
            $form->add('area', EntityType::class, [
                'class' => LocationArea::class,
                'choices' => $areas,
                'placeholder' => 'select now an area',
                'required' => false
            ]);
            }
        };

        $builder->get('country')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formArea){
                $country = $event->getForm()->getData();
                $formArea($event->getForm()->getParent(), $country);
            }
        );

        // formModifier
        $formDepartment = function(FormInterFace $form, LocationArea $area = null) {
            if($area != null) 
            {
                $departments = $area->getDepartments();
            $form->add('department', EntityType::class, [
                'class' => LocationDepartment::class,
                'choices' => $departments,
                'placeholder' => 'select now a department',
                'required' => false,
                'choice_label' => function ($departments) {
                    return '['.$departments->getCode().']-'.$departments->getName();
                }
            ]);
            }
        };

The area.addEventListener is removed by update area select, so it is necessary to re-activate the eventListener after update area select, so I create a function area_addEnventListener()

window.onload = () => {
    let country = document.querySelector("#location_country");

    country.addEventListener("change", function() {
        let form = this.closest("form");
        let data = this.name + "=" + this.value;
        fetch(form.action, {
            method: form.getAttribute("method"),
            body: data,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded; charset:UTF-8"
            }
        })
        .then(response => response.text())
        .then(html => {
            let content = document.createElement("html");
            content.innerHTML = html;
            let area = content.querySelector("#location_area");
            document.querySelector("#location_area").replaceWith(area);
            area_addEnventListener(area);
        })
        .catch(error => {
            console.log(error);
        })
    });

    let area = document.querySelector("#location_area");

    area_addEnventListener(area);
}

function area_addEnventListener(area_select) {
    area_select.addEventListener("change", function() {
        let formArea = area_select.closest("form");
        let dataArea = area_select.name + "=" + area_select.value;
        fetch(formArea.action, {
            method: formArea.getAttribute("method"),
            body: dataArea,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded; charset:UTF-8"
            }
        })
        .then(response => response.text())
        .then(html => {
            let contentArea = document.createElement("html");
            contentArea.innerHTML = html;
            let department = contentArea.querySelector("#location_department");
            document.querySelector("#location_department").replaceWith(department);
        })
        .catch(error => {
            console.log(error);
        })
    });
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.