This simple to use extension uses the jQuery plugin jquery-cascade to populate the data of a dependent dropdownlist by a ajax/getJSON-call.
It works with the standard CHtml::activeDropDownList or the CHtml/CActiveForm::dropDownList - no extra dropdown-components needed.
Requirements ¶
Developed with Yii 1.1.11, not tested with other releases
Usage ¶
Extract the zip-file into your to protected/extensions directory. Import the component in config/main.php
// autoloading model and component classes
'import'=>array(
'application.models.*',
'application.components.*',
......
'ext.cascadedropdown.ECascadeDropDown',
or use
Yii::import('ext.cascadedropdown.ECascadeDropDown');
in your controller instead.
A) Two activeDropDownLists ¶
Assume you have a model where you want to set the province and the city. But the city dropdown options depend on the selected province.
Your view ¶
Create your dropdownlists like you would do without this extension and add a line of code to make them "cascade". It's important to assign a unique id in the htmlOptions of the dropdownlists.
...
//add the province dropdownlist
$provinceItems = CHtml::listData(Province::model()->findAll(), 'id', 'name');
echo CHtml::activeDropDownList($model, 'province', $provinceItems, array('id'=>'id_province','prompt'=>'-'));
//add the cities dropdownlist, show only the items of the current province
$cityItems = CHtml::listData(City::model()->findAll('province=:province', array(':province'=>model->province)), 'id', 'name');
echo CHtml::activeDropDownList($model, 'city', $cityItems, array('id'=>'id_city','prompt'=>'-'));
//----------- the additional part ------------------------------------
//Build the dependency/cascade with province as master
//Show the text 'Loading cities ...' on waiting for the data.
//Update the city-data through the ajax-url: Yii::app()->createUrl('site/citydata')
ECascadeDropDown::master('id_province')->setDependent('id_city',array('dependentLoadingLabel'=>'Loading cities ...'),'site/citydata');
...
A change of the province-dropdownlist will lead to a ajax-call to populate the corresponding cities. So you have to create the action 'citydata' in the controller 'site'. In this function you can use some static ECascadeDropDown helper method to generate the json-data response. See the sourcecode for details.
Your controller ¶
class SiteController extends Controller
{
public function actionCitydata()
{
//check if isAjaxRequest and the needed GET params are set
ECascadeDropDown::checkValidRequest();
//load the cities for the current province id (=ECascadeDropDown::submittedKeyValue())
$data = City::model()->findAll('province=:province', array(':province'=>ECascadeDropDown::submittedKeyValue());
//Convert the data by using
//CHtml::listData, prepare the JSON-Response and Yii::app()->end
ECascadeDropDown::renderListData($data,'id', 'name');
}
That should be all ... Take a look at the comments in the source for more options.
B) Three dropDownLists ¶
If you want you can cascade three (or more) dropdownlists. Below is a ready to use example you can copy/paste for testing. It shows more ECascadeDropDown helper functions and uses the same controlleraction for all data-requests. If you want so, you have to set the controllerRoute in the master() method instead of the setDependent() method. But in most cases I would prefer to use extra controlleractions for each dropdownlist.
Your view ¶
Combine 3 dropdownlists:
$items = array('mobile'=>'Mobile','printers'=>'Printers');
echo CHtml::dropDownList('id_type','', $items, array('prompt'=>'-'));
echo CHtml::dropDownList('id_company', '', array());
echo CHtml::dropDownList('id_article', '', array());
//handle all data response in one controller action
ECascadeDropDown::master('id_type','site/selectarticle') //'controllerId/actionId'
->setDependent('id_company') //master for id_article with default options
->setDependent('id_article', //with custom labels
array('dependentStartingLabel'=>'Select article','dependentLoadingLabel'=>'Loading articles...'));
Your action in the SiteController ¶
public function actionSelectArticle()
{
ECascadeDropDown::checkValidRequest(); //comment to debug this url in the browser
$data = array(
'id_type' => array(
'mobile' => array('company_apple'=>'Apple','company_samsung'=>'Samsung'),
'printers' => array('company_hp'=>'HP','company_brother'=>'Brother'),
),
'id_company' => array(
'company_apple' => array('imac'=>'iMac','iphone'=>'iPhone','ipad'=>'iPad','ipod'=>'iPod'),
'company_samsung' => array('galaxy'=>'Galaxy','wave'=>'Wave'),
'company_hp' => array('laserjet'=>'Laserjet','photosmart'=>'Photosmart'),
'company_brother' => array('hlserie'=>'HL Serie','mwserie'=>'MW Serie'),
)
);
$masterId = ECascadeDropDown::submittedMasterId(); //master dropdownlist id_type or id_company
$masterKey = ECascadeDropDown::submittedKeyValue(); //the current key of the master
//$dependentId = ECascadeDropDown::submittedDependentId(); if you need the dependent id
if(isset($data[$masterId][$masterKey]))
ECascadeDropDown::renderArrayData($data[$masterId][$masterKey]); //with Yii::app()->end()
ECascadeDropDown::renderEmptyData('-');
}
Error
It lacks a closing parenthesis
$data = City::model()->findAll('province=:province', array(':province'=>ECascadeDropDown::submittedKeyValue());
fixed
$data = City::model()->findAll('province=:province', array(':province'=>ECascadeDropDown::submittedKeyValue()));
Usage
How to store this method used dropdownlist selected value in my current model
cascade dropdown in TbModal
Hi,
I am using yii bootstrap extension and trying to put the dropdown on a TbModal. How could I do that? I followed the tutorial, but not success yet.
Please help...
i got an empty result
i tried the three dropdownlist option,
in _form.php:
<?php echo CHtml::dropDownList('CycleID','', CHtml::listData(Usersectionsecurity::model()->findAll(), 'Group_Code', 'E_Group_Desc'), array( 'prompt'=>'Select Cycle', )); echo CHtml::dropDownList('stclass', '', array()); echo $form->dropDownList($model,'periodID', array()); ECascadeDropDown::master('CycleID','Usersectionsecurity/getClassByCycle') //'controllerId/actionId' ->setDependent('stclass') //master for id_article with default options ->setDependent('periodID', //with custom labels array('dependentStartingLabel'=>'Select Classes','dependentLoadingLabel'=>'Loading articles...')); ?>
in my controller:
public function actionGetClassByCycle() { ECascadeDropDown::checkValidRequest(); $type = Yii::app()->user->getType(); $userID = Yii::app()->user->getID(); if($type == 'Teacher'){ $model = Stclass::model()->GetClassOfUser($userID, ECascadeDropDown::submittedKeyValue()); }else{ $model=Usersectionsecurity::model()->findAll('Group_Code=\'' . ECascadeDropDown::submittedKeyValue() . '\''); } $models=Matiere::model()->GetMainSubjectByCycle(ECascadeDropDown::submittedKeyValue()); $data = array( 'CycleID' => CHtml::listData($model,'Class_Code', 'E_Class_Desc') , 'stclass' => CHtml::listData($models,'Subject_Code', 'E_Subject_Name') ); $masterId = ECascadeDropDown::submittedMasterId(); //master dropdownlist id_type or id_company $masterKey = ECascadeDropDown::submittedKeyValue(); //the current key of the master //$dependentId = ECascadeDropDown::submittedDependentId(); //if you need the dependent id if(isset($data[$masterId][$masterKey])) ECascadeDropDown::renderArrayData($data[$masterId][$masterKey]); //with Yii::app()->end() ECascadeDropDown::renderEmptyData('-'); }
i made a var_dump($data), i get values but they not appears in the appropriate dr
array(2) { ["CycleID"]=> array(3) { [24]=> string(7) "Grade 4" [25]=> string(7) "Grade 5" [26]=> string(7) "Grade 6" } ["stclass"]=> array(10) { ["020"]=> string(28) "French as a Foreign Language" ["040"]=> string(4) "Math" ["061"]=> string(9) "Geography" ["062"]=> string(7) "History" ["070"]=> string(16) "Cultural Studies" ["080"]=> string(6) "Civics" ["090"]=> string(18) "Physical Education" [110]=> string(3) "Art" [120]=> string(5) "Drama" [130]=> string(5) "Music" } } [{"value":"","label":"-"}]
where is my error. please help me
and with activeCheckBoxList ?
Thanks for this extension! It works great!
Now I would like to do a little different form. Is it possible to use this extension with an activeCheckBoxList (in which datas depend of the first dropdownlist) ?
Thanks for any ideas
checkboxlist
This extension uses jquery-cascade, which is implemented for dropdownlists only.
Thanks for this great little piece of code. I've made some minor improvements that I want to share to you folks.
A change in the php file ECascadeDropDown.php when you'll have multiple dependent dropdown lists with the same master list. (For example multiple employees with the same manager).
Change this code around line 82:
`
phpYii::app()->clientScript->registerScript('JQCascadeDropDown#' . $this->_masterId, $jsCode, $position);
to:
phpYii::app()->clientScript->registerScript('JQCascadeDropDown#' . $this->masterId . '' . $id, $jsCode, $position);
`
Note I made the combination of master and dependent dropdown list unique, else the last one will overwrite all others.
Two changes to the javascript file jquery.cascade-select.js:
`
php$('input').trigger('change')
You can use my complete sourceDdl.change() function:
`
javascriptsourceDdl.change(function() {
var extraParams = { timestamp: +new Date() }; $.each(options.extraParams, function(key, param) { extraParams[key] = typeof param == "function" ? param() : param; }); var data = $.extend({ selected: $(this).val() }, extraParams); // remember the value of the dependend in case it fetches the same value again var dependendentDdlValue = dependendentDdl.val(); dependendentDdl.empty() .attr('disabled', 'disabled') .append('<option>' + options.dependentLoadingLabel + '</option>'); if (options.spinnerImg) { dependendentDdl.next('.' + options.spinnerClass).remove(); var spinner = $('<img />').attr('src', options.spinnerImg); $('<span class="' + options.spinnerClass + '" />').append(spinner).insertAfter(dependendentDdl); } $.getJSON(options.source, data, function(response) { dependendentDdl.empty().attr('disabled', null); dependendentDdl.next('.' + options.spinnerClass).remove(); if (response.length > 0) { // Build options in the list and select the previously selected value if found again $.each(response, function(i, item) { dependendentDdl.append('<option value="' + item.value + '"' + (item.value == dependendentDdlValue ? ' selected="selected"' : '') + '>' + item.label + '</option>'); }); } else { dependendentDdl.empty() .attr('disabled', 'disabled') .append('<option>' + options.dependentNothingFoundLabel + '</option>'); } // trigger a change on this select after it has its new content dependendentDdl.change(); }); })
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.