If you are coming over to Yii 2 from Yii 1.x, you may have already read this useful wiki for creating dependent dropdowns. You can use a similar approach in Yii 2 to do the same. But if you are looking at a prebuilt solution that helps you manage it easier, read along.
Using DepDrop Widget ¶
This widget is part of the yii2-widgets package and specifically handles dependent dropdowns. 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/ORkartik\widgets\Select2 widget
.
Usage Steps ¶
Example ¶
- Let's assume you have 3 dropdown fields
cat
,subcat
, andprod
. subcat
depends oncat
prod
depends on bothcat
andsubcat
- 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 ¶
- You want to initialize the fields with preselected data.
- You must set the
data
property for the dependent fields that must contain the preselected value as a key. - Set the
depends
property forsubcat
andprod
as seen below - Set the
url
property to an action that will be called to render the list - Set the
initialize
property totrue
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. - Alternatively, you can also set the
data
property of each dropdown and not set theinitialize
property, so that no ajax calls are fired on document load.
use kartik\widgets\DepDrop;
use yii\helpers\Html;
// Parent
echo $form->field($model, 'cat')->dropDownList($catList);
// $catList is the initial list of values for `cat`
// Child # 1
echo $form->field($model, 'subcat')->widget(DepDrop::classname(), [
'data' => [2 => 'Music'], // ensure at least the preselected value is available
'pluginOptions'=>[
'depends'=>[Html::getInputId($model, 'cat')], // the id for cat attribute
'placeholder'=>'Select...',
'url'=>Url::to(['/site/subcat'])
]
]);
// Child # 2
echo $form->field($model, 'prod')->widget(DepDrop::classname(), [
'data' => ['2.2' => 'Product Music 2'], // ensure at least the preselected value is available
'pluginOptions'=>[
'depends'=>[
Html::getInputId($model, 'cat'), // the id for cat attribute
Html::getInputId($model, 'subcat'), // the id for subcat attribute
],
'placeholder'=>'Select...',
'url'=>Url::to(['/site/prod']),
'initialize'=>true
]
]);
Controller ¶
- 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. - Your controller action must read this and return a json encoded
subcat
orprod
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 demo.
Widget updating based on the last dropdown
Hi Kartik
As always, thank you very much for all your work, always looking forward for your next widget :-)
I would like to have a code example, if you can, for the following:
Let's say I have 2 dropdown lists, and I select in the first one a city, and in the next one, I select a bank name, e.g. citybank (one of many that exist in that city), I would like to have below these 2 dropdown lists a description of that bank, e.g. how many branches it has in this city.
Of course, this description should be updated when the last dropdown list is updated, and should be empty when nothing is selected in the last dropdown list.
Thanks!
Zvi.
Updating other fields
@zvik2004 you need to write your own javascript method for the onchange event for the last dropdown that will trigger an ajax call to fetch description and set the description field value.
Loading of initial value
Hello,
I use the widget in a CRUD form and I got everything working fine, but when I return in the form to edit the model, the selected value does not seem to be selected, even if I call with initialize=true. The option values are all loaded fine, but the selected value is not selected. Am I missing something? Do I have to do that myself with some javascript?
Thanks for all your wonderful widgets. - P.
Re: Loading of initial value
@Pierre the value of your field (selected value) for an UPDATE scenario, should be set or derived from your server code. For example, if your attribute is named
subcat
and you are using with a model, then the value of$model->subcat
will determine your selected value. If you are not using with a model, then you must pass thevalue
property to the widget.on update or after server validation return(some validation failed), load depended child with preselected value
I have change the following line
'data' => [2 => 'Music'] to
'data' =>($model->cat_id)?ArrayHelper::map(subcat::find()->where('cat_id=:cat_id',[':cat_id'=>$model->cat_id])->asArray()->all(), 'subcat_id', 'subcat_name'):[],
Not able to see the response data in depdropdown.
Hi Kartik,
I am getting the below error when i use DepDrop Can you please help me.
Error:
I am getting the following response, but not loading the doctor dropdown based on the doctor type.
{"output":[{"id":"79","first_name":"doctor abc"}],"selected":""}
but it show the blank.
_form.php
<?php $dataList1=ArrayHelper::map(\app\models\sysconfig\CodeDoctorTypeRecord::find()->asArray()->all(), 'id', 'code_doctor_type_desc'); echo $form->field($model, 'code_doctor_type_idn')->dropDownList($dataList1) ?> <?= $form->field($model, 'doctor_idn')->widget(DepDrop::classname(), [ 'options'=>['id'=>'subcat-id', 'class'=>'input-large form-control'], 'pluginOptions'=>[ //'depends'=>['cat-id'], 'depends'=>[Html::getInputId($model, 'code_doctor_type_idn')], // the id for cat attribute 'placeholder'=>'Select...', 'url'=>Url::to(['subcat']) ] ]); ?>
DoctorRecord.php
public static function getOptionsbyType($doctor_type_id) { $data = DoctorRecord::find()->where(['code_doctor_type_idn'=>$doctor_type_id])->select(['id','first_name'])->asArray()->all(); $value = (count($data) == 0) ? ['' => ''] : $data; return $value; }
OrderControler.php
public function actionSubcat() { $out = []; if (isset($_POST['depdrop_parents'])) { $parents = $_POST['depdrop_parents']; if ($parents != null) { $cat_id = $parents[0]; $out = \app\models\doctor\DoctorRecord::getOptionsbyType($cat_id); echo Json::encode(['output' => $out, 'selected' => '']); return; } } echo Json::encode(['output' => '', 'selected' => '']); }
Thanks,
Thani
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.