Dependent Select Dropdown

Serghei Iakovlev edited this page Dec 20, 2015 · 5 revisions

Sometimes you will need to create a dropdown that relies on another dropdown. A great example is when you have a list of countries that may display a list of states in another dropdown.

###Setting up the form Selects We will need two different dropdowns. Since the first drop down is independent and doesn't rely on anything else, we can automatically populate the dropdown with data.

use Project\Module\Models\Nations;

$nations = Nations::find([
    'columns' => 'nation_id, nation_name',
    'cache'   => ['key'  => 'nations', 'lifetime' => 86400]
]);

$nationDropDown = new Select(
    'nation_id',
    $nations,
    [
        'useEmpty'  => true,
        'emptyText' => 'None Specified',
        'using'     => ['nation_id', 'nation_name']
    ]
);

The second dropdown, we won't specify anything, since it is dependent on the first dropdown. JavaScript will automatically populate this dropdown with the correct values.

$statesSelect = new Select('states_id', [null => 'None Specified']);

In the view file

Since I like to keep my JavaScript separate from the rest of the view I typically put the JavaScript in its own file. So there are three changes in the view.

<script type="text/javascript">
    /*Define URLS to use in actions*/
    var getResultsUrl = "{{ grabResults }}";
</script>

The first javascript call allows us to grab the controller URL that will do the processing of the AJAX request for the dependent dropdown.

{{ javascript_include("js/referrals.js") }}

$("#nation_id").change(function() {
    var value = $(this).val();

    $.ajax({
        type: "POST",
        url: getResultsUrl,
        data: {"id": value},
        success: function(response){
            $("#states_id option")
                .not(":first").remove();

            parsed = $.parseJSON(response);

            $.each(parsed, function(key, value) {
                $("#states_id")
                    .append($("<option></option>")
                    .attr("value",value.id)
                    .text(value.name));
            });
        }
    });
});

This javascript file contains all the magic behind the first dropdown. The logic behind it is: Once the user selects a value from the dependent dropdown, we grab the user value, and make an ajax request with the user id.

That request will be to the controller we setup later. If the request is successful, we get a response back. We remove all of the options in the dependent dropdown except the first one. We parse the response as JSON.

After we parse the JSON, we append the result into the select field.

{{ form.render('nationDropDown') }}

{{ form.render('statesSelect') }}

Setting up the dropdowns in the view

The Controller

public function grabStatesAction()
{

    $id = $this->request->getPost('id', 'int');

    $data = States::find([
        'columns'   =>  ['state_id, state_name'],
        'conditions'=>  'state_parent_id = :id:',
        'bind'      =>  ['id' => $id]
    ]);

    foreach ($data as $result) {
        $resData[] = ['id' => $result->state_id, 'name' => $result->state_name];
    }

    echo json_encode($resData);
}

The controller then grabs the id and finds the records in the database that match that nation id.

After that is done we loop through each result and store it in an array. Finally, we spit out the date.

Extra - Repopulating the form field

The code above worked great for me when running through the forms the first time. On subsequent runs if the nation was already set it would not repopulate the state dropdown correctly. I wanted to extend its functionality so that I could use it on a user profile page and the state dropdown would repopulate with the correct state if chosen.

First we have to pass our object into the form that contains our dropdowns:

$this->view->form = new \Project\Module\Forms\UserProfileForm($user, array());
class UserProfileForm extends Form
{

    public function initialize($entity = null, $options = null)

Then when we render our state dropdown we check to see if our $entity was passed:

use Project\Module\Models\States;

if (isset($entity)) {
    $states = States::find([
        'columns'   => ['state_id, state_name'],
        'conditions'=> 'state_parent_id = :id:',
        'bind'      => ['id' => $entity->nation_id]
    ]);

    $state = new Select(
        'state_id',
        $states,
        [
            'useEmpty'  => true,
            'emptyText' => 'None Specified',
            'using'     => ['state_id', 'state_name']
        ]
    );
} else {
    $state = new Select('state_id', [null => 'None Specified']);
}

$state->setLabel('State');

$this->add($state);
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.