How to Create a Dependent Dropdown in Yii2?

  Add to Bookmark

Problem Statement

  • Create a form with dependent dropdowns:
  • Select Country, then it should update States
  • Select State, then it should update Cities

Solution Overview

We'll cover 4 different methods to solve this, each with increasing levels of abstraction and flexibility.


Methods Covered

Method #ApproachUses JSUses WidgetReturn FormatSuitable For
1jQuery Ajax + JSON stringYesNoJSON stringBeginners, learning
2Yii2-style JSON responseYesNoJSON objectClean apps
3Kartik DepDropMinimalYesJSON objectProduction use
4Inline onchange + HTML echoYesNoRaw HTMLFast dev/search forms

Method 1: Basic jQuery Ajax + JSON String

Controller (SiteController.php)

public function actionGetStates($country_id)
{
    $states = State::find()
        ->where(['country_id' => $country_id])
        ->asArray()
        ->all();

    return \yii\helpers\Json::encode($states);
}

View

<?= $form->field($model, 'country_id')->dropDownList(
    ArrayHelper::map(Country::find()->all(), 'id', 'name'),
    ['prompt' => 'Select Country', 'id' => 'country-id']
) ?>

<?= $form->field($model, 'state_id')->dropDownList([], [
    'prompt' => 'Select State',
    'id' => 'state-id'
]) ?>

JavaScript

$('#country-id').on('change', function () {
    $.get('/site/get-states?country_id=' + $(this).val(), function (data) {
        let options = '<option value="">Select State</option>';
        $.each(JSON.parse(data), function (i, item) {
            options += '<option value="' + item.id + '">' + item.name + '</option>';
        });
        $('#state-id').html(options);
    });
});

Method 2: Yii2 Native JSON Response

Controller

public function actionGetStates($country_id)
{
    Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    return ArrayHelper::map(
        State::find()->where(['country_id' => $country_id])->all(),
        'id',
        'name'
    );
}

View

<?= $form->field($model, 'country_id')->dropDownList(
    ArrayHelper::map(Country::find()->all(), 'id', 'name'),
    ['prompt' => 'Select Country', 'id' => 'country-id']
) ?>

<?= $form->field($model, 'state_id')->dropDownList([], [
    'prompt' => 'Select State',
    'id' => 'state-id'
]) ?>

JavaScript

$.get('/site/get-states?country_id=' + $(this).val(), function (data) {
    let options = '<option value="">Select State</option>';
    $.each(data, function (id, name) {
        options += `<option value="${id}">${name}</option>`;
    });
    $('#state-id').html(options);
});

Method 3: Using Kartik DepDrop Widget

Install Kartik DepDrop

composer require kartik-v/yii2-widget-depdrop "@dev"

Controller

public function actionSubcat()
{
    Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    $out = [];

    if (isset($_POST['depdrop_parents'])) {
        $parents = $_POST['depdrop_parents'];
        if ($parents !== null) {
            $country_id = $parents[0];
            $out = State::find()
                ->where(['country_id' => $country_id])
                ->select(['id AS id', 'name AS name'])
                ->asArray()
                ->all();
            return ['output' => $out, 'selected' => ''];
        }
    }

    return ['output' => '', 'selected' => ''];
}

View

use kartik\depdrop\DepDrop;
use yii\helpers\Url;

<?= $form->field($model, 'country_id')->dropDownList(
    ArrayHelper::map(Country::find()->all(), 'id', 'name'),
    ['id' => 'country-id']
); ?>

<?= $form->field($model, 'state_id')->widget(DepDrop::classname(), [
    'options' => ['id' => 'state-id'],
    'pluginOptions' => [
        'depends' => ['country-id'],
        'placeholder' => 'Select State...',
        'url' => Url::to(['/site/subcat'])
    ]
]); ?>

Method 4: Inline Ajax in DropDown onchange (Returns Raw HTML)

View

<?= $form->field($model, 'state_id')->dropDownList(\yii\helpers\ArrayHelper::map(MasterState::find()->all(), 'id', 'state_name'),
    [
        'prompt' => 'Select State',
        'onchange' => '
            $.get("/site/citylist?state_id=" + $(this).val(), function(data) {
                $("select#city_id").html(data);
            });
        '
    ]
); ?>

 <?= $form->field($model, 'city_id')->dropDownList(
        \yii\helpers\ArrayHelper::map(MasterCity::find()->where(['state_id' => $model->state_id])->all(), 'city_id', 'city_name'),
        ['prompt' => 'Select City','id'=>'city_id']
   ); ?>

Controller

public function actionCitylist($state_id)
{
    $cities = MasterCity::find()
        ->where(['state_id' => $state_id])
        ->all();

    echo "<option value=''>Select City</option>";
    foreach ($cities as $city) {
        echo "<option value='" . $city->id . "'>" . $city->city_name . "</option>";
    }
}

Conclusion

  • Use Method 1 or 2 for learning and control.
  • Use Method 3 (DepDrop) for cleaner, production-ready forms.
  • Use Method 4 when building quick filters or search tools.