Yii 1.1: Creating a dependent dropdown

94 followers

Often you'll need a form with two dropdowns, and one dropdown's values will be dependent on the value of the other dropdown. Using Yii's built-in AJAX functionality you can create such a dropdown.

The view with the form.

We'll show a form that shows countries and dependent of the country selected will show cities.

echo CHtml::dropDownList('country_id','', array(1=>'USA',2=>'France',3=>'Japan'),
array(
'ajax' => array(
'type'=>'POST', //request type
'url'=>CController::createUrl('currentController/dynamiccities'), //url to call.
//Style: CController::createUrl('currentController/methodToCall')
'update'=>'#city_id', //selector to update
//'data'=>'js:javascript statement' 
//leave out the data key to pass all form values through
))); 
 
//empty since it will be filled by the other dropdown
echo CHtml::dropDownList('city_id','', array());

The first dropdown is filled with several value/name pairs of countries. Whenever it is changed an ajax request will be done to the 'dynamiccities' action of the current controller. The result of that request (output of the 'dynamiccities' action) will be placed in the second dropdown with id is #city_id.

The controller action

It will have to output the html to fill the second dropdownlist. Furthermore it will do that dependent on the the value of the first dropdown.

public function actionDynamiccities()
{
    $data=Location::model()->findAll('parent_id=:parent_id', 
                  array(':parent_id'=>(int) $_POST['country_id']));
 
    $data=CHtml::listData($data,'id','name');
    foreach($data as $value=>$name)
    {
        echo CHtml::tag('option',
                   array('value'=>$value),CHtml::encode($name),true);
    }
}

It will retrieve all cities that have as a parent the id of the first dropdown. It will then output all those cities using the tag and the output will end up in the second dropdown.

You might wonder where the $_POST['country_id'] comes from. It's simple, when the 'data' key of the ajax array in the first dropdown is empty, all values of the elements of the form the dropdown is in, will be passed to the controller via the ajax request. If you're using Firebug you can inspect this request and see for yourself.

This behaviour can also be changed. By default the value of the 'data' key in the ajax configuration array is js:jQuery(this).parents("form").serialize(). The preceding js: indicates to Yii that a javascript statement will follow and thus should not be escaped. So, if you change the 'data' key to something else preceded by 'js:' you can fill in your own statement. The same applies to the 'success' parameter.

For this to work you also need to edit the Method accessRules() (if available) in your current Controller. In this example we would change

array('allow', // allow authenticated user to perform 'create' and 'update' actions
                'actions'=>array('create','update'),
                'users'=>array('@'),
            ),

to

array('allow', // allow authenticated user to perform 'create' and 'update' actions
                'actions'=>array('create','update','dynamiccities'),
                'users'=>array('@'),
            ),

in order to allow access for authenticated Users to our Method 'dynamiccities'. To allow access to eg any User or to use more complex rules please read more about accessRules and Authentication here.

Note: For testing purposes you could also comment out

'accessControl', // perform access control for CRUD operations

in the filters() Method. This will disable all access controls for the controller. You should always re-enable it after testing.

Related Links

Persian version

Total 20 comments

#16927 report it
Mahdi Miad at 2014/04/14 05:38am
Using CActiveForm with a Model

To use this article using model data here are the changes: at the View

echo $form->dropDownList($model, 'country_id' ,array(1=>'USA',2=>'France',3=>'Japan'),
array(
'ajax' => array(
'type'=>'POST', //request type
'url'=>CController::createUrl('currentController/dynamiccities'), //url to call 
'update'=>'#'.CHtml::activeId($model,'city_id'), 
))); 
 
echo $form->dropDownList(($model, 'city_id',  array());

And at the Controller

public function actionDynamiccities()
{
    $data=Location::model()->findAll('parent_id=:parent_id', 
           array(':parent_id'=>(int) $_POST['currentController'] ['country_id']));));
 
    $data=CHtml::listData($data,'id','name');
    foreach($data as $value=>$name)
    {
        echo CHtml::tag('option',
                   array('value'=>$value),CHtml::encode($name),true);
    }
}

As you can see the only change at the controller is $_POST['currentController'] ['country_id'] instead of $_POST['country_id']

#16319 report it
kristantoarman at 2014/02/10 04:15am
set 1 textfield from 2 dropdownlist selected value

could you help me for this:

settxt

this form :

<tr>
        <td><?php echo $form->labelEx($model,'kd_jenisdokumen'); ?></td>
                <td><?php echo $form->dropDownList($model,'kd_jenisdokumen',CHtml::listData(MsJenisdokumen::model()->findAll(),
                    'kd_jenisdokumen','deskripsi'),
                    array(
                        'prompt'=>'Pilih Jenis',
                        'ajax'=>array(
                            'empty'=>'Pilih Jenis',
                            'type'=>'POST',
                            'url' => CController::createUrl('jenis'),
                            'data'=> array('jdok'=>'js:this.value'),
                            'update'=>'#jendok',
                            ))
                    );
                ?>
                </td>
        <td><?php echo $form->error($model,'kd_jenisdokumen'); ?></td>
            </tr>
            <tr>
        <td><?php echo $form->labelEx($model,'kd_departemen'); ?></td>
                <td><?php echo $form->dropDownList($model,'kd_departemen',CHtml::listData(MsDepartemen::model()->findAll(),
                    'kd_departemen','deskripsi'),
                    array(
                        'empty'=>'Pilih Departemen',
                        'ajax'=>array(
                            'type'=>'POST',
                            'url' => CController::createUrl('dept'),
                            'data'=> array('dept'=>'js:this.value'),
                            'update'=>"#dept",
                            ))
                    );
                    ?>
                </td>
                <td><?php echo $form->error($model,'kd_departemen'); 
                ?></td>
            </tr>
            <tr>
                <td>
                </td>
                <td id="jendok"></td>
                <td></td>
            </tr
            <tr>
                <td></td>
                <td id="dept"></td>
                <td></td>
            </tr>
            <tr>
        <td><?php echo $form->labelEx($model,'no_dokumen'); ?></td>
                <td><?php echo $form->textField($model,'no_dokumen',array('id'=>'no_dokumen','size'=>20,'maxlength'=>20,));
                          echo CHtml::Button('check',array('onclick'=>"{check();}"));
                    ?>
</td>
        <td><?php echo $form->error($model,'no_dokumen',array('id'=>'no_dokumen')); ?></td>

this controller :

// function untuk mengambil POST jenis dokumen
        public function actionJenis(){
            $data = $_POST['jdok'];
            echo CHtml::textField ('jendok', 'BUT-'.$data.'/');
        }
 
        // function untuk mengambil POST departemen
        public function actionDept(){
            $data2 = $_POST['dept'];
            echo CHtml::textField ('dept', $data2.'-');
        }

I want to set just 1 textfield in no dokumen. Please help thx...

#16139 report it
sercio at 2014/01/23 01:05am
Thanks, just one question

Hi, thank you this is a great tutorial.

Just a quick question. In the controller, why it is like this:

public function actionDynamiccities()
{
    $data=Location::model()->findAll('parent_id=:parent_id', 
                  array(':parent_id'=>(int) $_POST['country_id']));
}

and not like this:

public function actionDynamiccities()
{
    $data=Location::model()->findAll('parent_id=' . $_POST['country_id']);
}

Second one also seems to work in my system.

#16089 report it
Patrice at 2014/01/18 02:25pm
Thanks

I successfully followed your method. Thanks to Firebug, as you said, I saw that in the action, the values are named :

$_POST['Mymodel']['myfield']

Thanks again. Nice explanations.

#15999 report it
Amazing at 2014/01/08 06:30am
Ajax dropdown empty upon validation errors - fixed

I had issues with the ajax dropdowns upon validation errors on submit (post back) and this is how I managed to solve it. (my dependency was between banks and their branches)

in my Branch model:

public static function getBranches($bank_id)
    {
        $data=Branches::model()->findAll('BankRoutingNumber=:BankRoutingNumber', 
                      array(':BankRoutingNumber'=> $bank_id));
        return CHtml::listData($data,'BranchRoutingNumber','Name');
    }

In my Transactions controller:

public function actionDynamicBranches()
    {
        if(isset($_POST['Bank_ID']) ) 
        {
            $data = Transactions::model()->getBranches($_POST['Bank_ID']);
            echo "<option value=''>Select a Branch</option>";
            foreach($data as $value=>$name)
            {
                echo CHtml::tag('option',
                           array('value'=>$value),CHtml::encode($name),true);
            }
        }
    }

In my view:

echo $form->labelEx($model,'PayorBankRoutNumber'); 
 
     if(isset($_POST['Bank_ID'])) $bankid = $_POST['Bank_ID']; else $bankid = '';
     $list = CHtml::listData(Banks::model()->findAll(), 'RoutingNumber', 'Name');
     echo CHtml::dropDownList('Bank_ID', $bankid , $list, array(
            'ajax' => array(
            'type'=>'POST', //request type
            'url'=>CController::createUrl('transactions/dynamicbranches'), //url to call.
            'update'=>'#' . CHtml::activeId($model, 'PayorBankRoutNumber'), //selector to update
            'data'=>array('Bank_ID'=>'js:this.value') 
            //leave out the data key to pass all form values through
            ),'empty'=>'Select a Bank','style'=>'width:44%')
        ); 
 
    // get list if not empty
    $listBranches = array();
    if(!empty($bankid)) 
        {
            $listBranches = Transactions::model()->getBranches($bankid);            
        }
     echo CHtml::dropDownList(CHtml::activeName($model, 'PayorBankRoutNumber'), $model->PayorBankRoutNumber, $listBranches, array('style'=>'width:45%'));

The idea is to give an empty array to the second dropdown if POST PARENT ID is not set. If POST PARENT ID is set, then call the branch method directly to get the list and Yii model's binding will do the magic to set the appropriate selected values.

Hope it helps someone.

References: http://stackoverflow.com/questions/6169413/how-to-load-the-previously-filled-fields-when-model-validation-in-yii-fails

#15578 report it
Scotter at 2013/11/24 09:40am
Corrections

For the benefit of the next newbie who comes across this page, this should hopefully solve a few of your problems trying to get this implemented.

1) If 'data' is not specified in the ajax array then it is by default send all the attributes on all the forms in an array, e.g.

$_POST = 
(
    'Country' => array
    (
        'id' => '2'
        'country_id' => '1'
        'attib1' => '1'
        'attib2' => '1'
        'attib3' => '1'
    )
    'User' => array
    (
        'fullname' => 'My Users Full Name'
    )

(assuming Country was the name of the parent model). I would suggest setting data like this

'data'=>array('country_id'=>'js:this.value')

which will result in only sending the one parameter you need - quicker, less bandwidth OR change

array(':parent_id'=>(int) $_POST['country_id']));

to

array(':parent_id'=>(int) $_POST['Country']['country_id']));

(assuming Country was the name of the parent model)

2) The

'update'=>'#city_id', //selector to update

should be

'update'=>'#' . CHtml::activeId($model, 'city_id'), //selector to update

to create the HTML id pragmatically or you do it manually by using

'update'=>'#Country_city_id, //selector to update

(assuming Country was the name of the parent model)

I suspect these issues have arisen as a result of version changes. Article was posted Mar 22, 2009 and I'm using v1.1.14 which was released on Aug 11, 2013

#15165 report it
Shahcheraghean at 2013/10/14 06:34am
@wesh ask in forum

Hi there, I suggest you ask your question in Yii forum, you get answer more faster.

#15149 report it
wesh at 2013/10/12 03:12am
4 depedent dropdownlist with preselected values not working

I have 4 dropdownlist which are working like charm except when i have preselect values passed from the controller like this

public function actionBodyTrim(){
        $trim = new CarTrim;
        $spares = new Spares;
 
        $spares->makeid  = $_REQUEST['mid'];
        $spares->modelid = $_REQUEST['moid'];
        $spares->bodyid  = $_REQUEST['bid'];
        $spares->trimid  = $_REQUEST['tid'];
 
        $this->render('bodytrim', array(
                      'trim'=>$trim,
                      'spares'=>$spares
                      )
        );
    }

When the page is rendered the first dropdownlist is displaying the preselected value. i have tested the second dropdownlist with the code below but it does not fire the Ajax Calls. The result is shown below

<div class="span5 leftpull">
        <div class="row">
         <div class="width125"><?php echo $form->labelEx($model,'makeid'); ?></div>
         <div class="widthtxt"><?php echo $form->dropDownList($model,'makeid', CHtml::listData(Makes::model()->findAll(array('order' => 'makename ASC')), 'makeid', 'makename'),
                                         array('prompt'=>'Select Makes',
                                               'ajax'=>array('url'=>CController::createUrl('CarModels'),
                                                'beforeSend' => 'function(){$("#carmodels").addClass("loading");}',
                                                'complete' => 'function(){$("#carmodels").removeClass("loading");}',
                                               'type' =>'POST',
                                               'update'=>'#'. CHtml::activeId($model, 'modelid'),
                                               array('class'=>'ajaxlink'),
 
                                            ))); ?>
 
 
                    <?php echo $form->error($model,'makeid'); ?>
          </div>
    </div>
 
    <div class="row" id="carmodels">
         <div class="width125"><?php echo $form->labelEx($model,'modelid'); ?></div>
         <div class="widthtxt"><?php echo $form->dropDownList($model, 'modelid', empty($model->modelid) ? array('prompt' => 'Select the country first') :
                                          array('ajax'=>array('type'=>'POST',
                                                              'url'=>CController::createUrl('BodyType'),
                                                              'update'=>'#'. CHtml::activeId($model, 'bodyid'),
                                                              'prompt'=>'Models',
                                                              array('class'=>'ajaxlink'),
 
                                                   ))); 
            ?>
            <?php echo $form->error($model,'modelid'); ?>
        </div>
    </div>

HMTL code of the Ajax not firing as required.

<div class="row" id="carmodels">
         <div class="width125"><label for="Spares_modelid" class="required">Model <span class="required">*</span></label></div>
         <div class="widthtxt"><select name="Spares[modelid]" id="Spares_modelid">
<optgroup label="ajax">
<option value="type">POST</option>
<option value="url">/spareparts/index.php?r=spares/parts/BodyType</option>
<option value="update">#Spares_bodyid</option>
<option value="prompt">Models</option>
<optgroup label="0">
<option value="class">ajaxlink</option>
</optgroup>
</optgroup>
</select>                   </div>
    </div>

Where i am getting it wrong??

Kind Regards

#15125 report it
freshyiiuser at 2013/10/09 04:01pm
three drop down list - 1 slave depends on 2 masters dropdownlist

is there any possibility to apply this methods on 3 dropdownlist in condition that the first 2 dropdownlists affect a third one?

kindly i need this solution urgently

#15018 report it
Wiwit Iwan SEP at 2013/09/29 09:46pm
Three Layer Dependent Dropdownlist

Is there anyway I can make three layer dependent dropdownlist? I've tried but no luck.

#14831 report it
Gajanan at 2013/09/13 06:46am
Use IN Condition in Mysql
echo $form->dropDownList($model,'mat_id',CHtml::listData(Material::model()->findAll(),'mat_id','mat_name'),

I have to pass array like below query

"Select mat_id,mat_name from material where mat_id IN array(1,2,3) " Please how to write

#14396 report it
RvR at 2013/08/08 07:24am
Updates in text box

For updating textbox value based on drop down menu selection, what changes needed in form and controller action?

thanks.

#14298 report it
freshyiiuser at 2013/08/02 08:51pm
load dropdownlist depend on 2 other parent dropdownlist

is this case covert the case of if i have 2 dropdownlist that affect a third one.

ex: i have cycle, behavior type and behavior

the value of the behavior is depending of the both value of the cycle AND the behavior.

means that i have to select the cycle and the type to get the true value of behavior.

is there any example of this case or it's not cover here?

thank you

#13537 report it
Ventoh at 2013/06/04 11:38am
city should be pre-filled, based on country
<?php echo $form->dropDownList($model, 'city_id', empty($model->country_id) ? 
                        array ( 'prompt' => 'Select the country first' ) : CHtml::listData( Cities::model()->findAll(
                        array(
                            'condition' => 'country_id=:country_id', 
                            'params'=>array(':country_id' => $model->country_id)
                        )), 'id', 'name')
                      ); ?>
#13455 report it
freshyiiuser at 2013/05/29 11:11am
$form dropdownlist problem

the code is working fine for me. but if i write $form->dropdownlist instead of CHtml::dropdownlist in the second list the data is not appeared, even the data is appeared in the firebug i write teh code like this

//  select the Behavior Group 
            echo $form->dropDownList($model,'behavGroupID',  array(),
                    array(
                    'ajax' => array(
                        'type'=>'POST', 
                        'url'=>CController::createUrl('Assignment/getBehavior'), //url to call.
                        'update'=>'#behavID',
                    ))); 
 
            echo $form->error($model,'behavGroupID');
            echo "&nbsp &nbsp";
 
            //  select the Behavior
            echo $form->dropDownList($model,'behavID',array(),array('prompt'=>'Select Behavior'));

the controller is

public function actionGetBehavior()
    {
        $behavior = Behavior::model()->findAll('behavGroupID=\'' . $_POST['Assignment']['behavGroupID'] . '\'');
        $data=CHtml::listData($behavior,'ID','E_Name');
 
        foreach($data as $value=>$name)
        {
            echo CHtml::tag('option',array('value'=>$value),CHtml::encode($name),true);
        }
    }

how to solve this issue?

#13341 report it
freshyiiuser at 2013/05/23 04:09am
the second dropdownlist is empty

hi, thank you for this example, actually that what i was need. i applied this example in my form but it gives me an empty this is my code

in form view:

echo $form->dropDownList($model,'BehavType', array(Yii::app()->params['bType']),array('empty'=>'Select Type'),
                                    array(
                                    'ajax' => array(
 
                                    'type'=>'POST', //request type
                                    'url'=>CController::createUrl('getBehaviorGroup'), //url to call.
                                    'data'=>array('BehavType'=>'js:this.value'),
                                    'update'=>'#behavGroupID',
                                    ))); 
 
echo $form->dropDownList($model,'behavGroupID',  array(),  array('prompt'=>'Select Behavior Group'));

in controller:

public function actionGetBehaviorGroup()
    {
        $category=Category::model()->GetCatOfType($_POST['BehavType']);
        foreach ($category as $cat){
 
            $data=Catcyclegroupbehav::model()->GetBehGroupOfCat($cat, 1);
        }
 
        $data=CHtml::listData($data,'ID','E_BGName');
        foreach($data as $value=>$name)
        {
            echo CHtml::tag('option',array('value'=>$value),CHtml::encode($name),true);
        }
 
    }

i don't know what is the problem. can anyone tell me

#12068 report it
ram87 at 2013/02/26 12:23am
4 daimention list box generate in yii

view: <?php /* @var $this RamCityController */ /* @var $model RamCity */ /* @var $form CActiveForm */ ?>

<?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'ram-city-form', 'enableAjaxValidation'=>false, )); ?>

Fields with * are required.

<?php echo $form->errorSummary($model); ?>
<?php echo $form->labelEx($model,'Continent'); ?> <?php /*echo CHtml::dropDownList('con_id','con_id',CHtml::listData(RamContinent::model()->findAll(),'con_id','description'), array( 'ajax'=>array( 'type'=>'POST', 'url'=>Ccontroller::createUrl('RamCity/Selectcounty'), 'update'=>'#'.CHtml::activeId($model,'c_id'), ), 'prompt'=>'select Continet', ));*/ ?> <?php echo $form->dropDownList($model,'con_id',CHtml::listData(RamContinent::model()->findAll(),'con_id','description'), array( 'ajax'=>array( 'type'=>'POST', 'url'=>Ccontroller::createUrl('RamCity/Selectcounty'), 'update'=>'#'.CHtml::activeId($model,'c_id'), ), 'prompt'=>'select Continet', )); ?> <?php echo $form->error($model,'con_id'); ?>
<?php echo $form->labelEx($model,'country'); ?> <?php /*echo CHtml::dropDownList('c_id','c_id',CHtml::listData(RamCountry::model()->findAll(),'c_id','description'), array( 'ajax'=>array( 'type'=>'POST', 'url'=>Ccontroller::createUrl('RamCity/SelectState'), 'update'=>'#'.CHtml::activeId($model,'s_id'), ), 'prompt'=>'select Country', ));*/ ?> <?php echo $form->dropDownList($model,'c_id',array(), array( 'ajax'=>array( 'type'=>'POST', 'url'=>Ccontroller::createUrl('RamCity/SelectState'), 'update'=>'#'.CHtml::activeId($model,'s_id'), ), 'prompt'=>'select Country', )); ?> <?php echo $form->error($model,'c_id'); ?>
<?php echo $form->labelEx($model,'State'); ?> <?php /*echo CHtml::dropDownList('s_id','s_id',CHtml::listData(RamState::model()->findAll(),'s_id','description'), array( 'ajax'=>array( 'type'=>'POST', 'url'=>Ccontroller::createUrl('RamCity/SelectCity'), 'update'=>'#'.CHtml::activeId($model,'ct_id'), ), 'prompt'=>'select Country', ));*/ echo $form->dropDownList($model,'s_id',array(), array( 'ajax'=>array( 'type'=>'POST', 'url'=>Ccontroller::createUrl('RamCity/SelectCity'), 'update'=>'#'.CHtml::activeId($model,'ct_id'), ), 'prompt'=>'select Country', )); ?> <?php echo $form->error($model,'s_id'); ?>
<?php echo $form->labelEx($model,'ctiy'); ?> <?php echo $form->dropDownList($model,'ct_id', array(), array('empty'=>'select CITY')); ?> <?php echo $form->error($model,'ct_id'); ?>
<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
<?php $this->endWidget(); ?>

controller:

public function actionSelectcounty(){ echo $_POST['RamCity']['con_id']; $listdata = RamCountry::model()->findAll('con_id=:contryid',array(':contryid'=>(int)$_POST['RamCity']['con_id'])); $listdata = CHtml::listData($listdata,'c_id','description');

                echo CHtml::tag('option',array('value'=>''),'select Country',true);

                foreach($listdata as $value => $description){
                echo CHtml::tag('option',
                array('value'=>$value),CHtml::encode($description),true);

                }
                }
                public function actionSelectState(){

                        $listdata = RamState::model()->findAll('c_id=:contryid',array(':contryid'=>(int)$_POST['RamCity']['c_id']));
                        $listdata = CHtml::listData($listdata,'s_id','description');

                        echo CHtml::tag('option',array('value'=>''),'select State',true);

                        foreach($listdata as $value => $description){
                        echo CHtml::tag('option',
                        array('value'=>$value),CHtml::encode($description),true);

                        }
                }

                public function actionSelectCity(){

                        $listdata = RamCity::model()->findAll('s_id=:contryid',array(':contryid'=>(int)$_POST['RamCity']['s_id']));
                        $listdata = CHtml::listData($listdata,'ct_id','description');

                        echo CHtml::tag('option',array('value'=>''),'select City',true);

                        foreach($listdata as $value => $description){
                        echo CHtml::tag('option',
                        array('value'=>$value),CHtml::encode($description),true);

                        }
                }
#11081 report it
mrs at 2012/12/15 12:19am
dependent dropdownlist variable value is empty when submit to database

This is a great issue that I have seen in many posts in Yii forum. Also, I have tried to submit form with dependent dropdownist by following this wiki article but its always submit form by ignoring my selected value from dependent dropdownlist. Hope issue article will update about to fix this issue.

With Thanks,

mrs

#10508 report it
tpandi at 2012/11/01 08:22am
validation

Hi All,

  This method working fine, but i need validation using three dropdownlist selected three values. Now i change first one update only second dropdown not update third dropdown. How to do this three dropdown update validation.

Leave a comment

Please to leave your comment.

Write new article