Managing nested dropdown dependency with DepDrop widget

If you are coming over to Yii 2 from Yii 1.x, you may have already read this useful wiki for programming the dependency between HTML dropdown lists. You can use a similar approach in Yii 2 to do the same from scratch. But if you are looking at a ready-made solution and a prebuilt widget that helps you manage it easier, read along.

Using DepDrop Widget

This widget is part of the yii2-widgets package and specially handles dependent dropdowns. The widget is based on the dependent-dropdown jQuery plugin by Krajee. You can refer documentation and usage demos for this widget for details. The explanation of the examples is mentioned here for reference.

Tip

You can use the widget for rendering dependent dropdown lists using yii\helpers\Html::dropDownList AND/OR kartik\widgets\Select2 widget.

Usage Steps

Example

  1. Let’s assume you have 3 dropdown fields cat, subcat, and prod.
  2. subcat depends on cat
  3. prod depends on both cat and subcat
  4. Let’s assume you have these following initial preselected values for the dropdown fields
$model->cat = 2;
$model->subcat = 2;
$model->prod = '2.2';

View

  1. You want to initialize the fields with preselected data.
  2. You must set the data property for the dependent fields that must contain the preselected value as a key.
  3. Set the depends property for subcat and prod as seen below
  4. Set the url property to an action that will be called to render the list
  5. Set the initialize property to true for the last child in your nested list (i.e. prod in this case). This will enable you to tell the plugin to automatically fire the ajax calls on document load and generate the dropdowns data in the right order sequentially for the nested chain.
  6. Alternatively, you can also set the data property of each dropdown instead of setting the initialize property, so that no ajax calls are fired on document load. The widget in this case will directly use data to pre-populate the dependent dropdowns.
use kartik\widgets\DepDrop;
use yii\helpers\Html;
use yii\helpers\Url;

// Parent 
// $catList is the initial list of values for `cat`
echo $form->field($model, 'cat')->dropDownList($catList);  

// Child # 1
echo $form->field($model, 'subcat')->widget(DepDrop::classname(), [
    // ensure at least one default value is preselected
    'data' => [2 => 'Music'], 
    'pluginOptions'=>[
        // the id for cat attribute
        'depends'=>[Html::getInputId($model, 'cat')], 
        'placeholder'=>'Select...',
        'url'=>Url::to(['/site/subcat'])
    ]
]);

// Child # 2
echo $form->field($model, 'prod')->widget(DepDrop::classname(), [    
    // ensure at least one default value is preselected
    'data' => ['2.2' => 'Product Music 2'], 
    'pluginOptions'=>[
        'depends'=>[
            // the id for cat attribute
            Html::getInputId($model, 'cat'), 
            // the id for subcat attribute
            Html::getInputId($model, 'subcat'), 
        ],
        'placeholder'=>'Select...',
        'url'=>Url::to(['/site/prod']),
        'initialize'=>true
    ]
]);

Controller

  1. The DepDrop widget uses the dependent-dropdown plugin. This plugin
    triggers an ajax response on a parent dropdown change, and passes in an array variable depdrop_parents for each
    dependent field.
  2. Your controller action must read this and return a json encoded subcat or prod list based on dependencies as seen below.

Note: its important to adhere to the return format of the json encoded response as shown in the examples and in the widget documentation. This differs a bit if you are returning a list with optgroups.

// Generate list of subcat based on cat
public function actionSubcat() {
    $out = [];
    if (isset($_POST['depdrop_parents'])) {
        $parents = $_POST['depdrop_parents'];
        if ($parents != null) {
            $cat_id = $parents[0];
            $out = self::getSubCatList($cat_id); 
            /**
             * the getSubCatList function will query the database based on
             * the cat_id and return an array like below:
             * [
             *    ['id'=>'<sub-cat-id-1>', 'name'=>'<sub-cat-name1>'],
             *    ['id'=>'<sub-cat_id_2>', 'name'=>'<sub-cat-name2>']
             * ]
             */
            echo Json::encode(['output'=>$out, 'selected'=>'']);
            return;
        }
    }
    echo Json::encode(['output'=>'', 'selected'=>'']);
}

// Generate list of products based on cat and subcat
public function actionProd() {
    $out = [];
    if (isset($_POST['depdrop_parents'])) {
        $ids = $_POST['depdrop_parents'];
        $cat_id = empty($ids[0]) ? null : $ids[0];
        $subcat_id = empty($ids[1]) ? null : $ids[1];
        if ($cat_id != null) {
           $data = self::getProdList($cat_id, $subcat_id);
            /**
             * the getProdList function will query the database based on
             * the cat_id and sub_cat_id and return an array like below:
             *  [
             *      'out'=>[
             *          ['id'=>'<prod-id-1>', 'name'=>'<prod-name1>'],
             *          ['id'=>'<prod_id_2>', 'name'=>'<prod-name2>']
             *       ],
             *       'selected'=>'<prod-id-1>'
             *  ]
             */

            echo Json::encode([
                'output'=>$data['out'], 
                'selected'=>$data['selected']
            ]);
            return;
        }
    }
    echo Json::encode(['output'=>'', 'selected'=>'']);
}

And that’s pretty much it. You should see the dependent dropdowns working now automatically, as seen in the DepDrop widget usage and demo.

5 thoughts on “Managing nested dropdown dependency with DepDrop widget

  1. i install this properly but got error “PHP Fatal Error – yii\base\ErrorException

    Class ‘Url’ not found”
    guide me..

    1. You must use classes with namespaces in PHP versions > 5.4 — else you get the error. Check the statement use yii\helpers\Url; at the top.

      Usually if you get the Class not found error – just look if you have namespaced the class correctly.

  2. Great work, Thanks. Why do we have to use ‘data’ option to set saved values? Since we are using modal attribute it should set the correct value if the drop-down is initialized.

    Anyway, I am using ‘data’ => [$model->adm_district_id => ”], to set the value and it is working. Is this the correct approach?

    1. Not sure what you are trying. The data property is used to set the array for the list of options for the dropdown. It has nothing to do with the saved value of the input.

Comments are closed.