You are viewing revision #10 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.
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()
{
//please enter current controller name because yii send multi dim array
$data=Location::model()->findAll('parent_id=:parent_id',
array(':parent_id'=>(int) $_POST['Current-Controller']['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.
Data attribute
Here is an example showing passing two values for the data element -- the first variable is named "language_id" and will pass the value dynamically (via JavaScript) of an HTML select element with an id of "language_id"; the second variable is named "foo" and will pass a static value of "bar":
'data'=>array('language_id'=>'js:$(\'#language_id\').val()', 'foo'=>'bar'),
You should be able to sort out what you need based on that. If you want to pass all the values in your form, just leave the data attribute blank, as in the example.
To form or not to form
For everyone how found difficulties in above tutorial I'd like to share my insights. Once you put both CHtml::dropDownList between <form> tags this example works like a charm. If not it will not work. The reason for that is that CHtml:ajax function use as default 'data' value the following statement:
~~~
[javascript]
js:jQuery(this).parents("form").serialize()
~~~
Means it looks for "form" parent.
Alternatively you can basically override the 'data' attribute with the following:
~~~
[javascript]
js:jQuery(this).serialize()
~~~
This cause the form is not necessary any more. The option value is taken directly from dropDownList element.
here's the version that helped me, based from that example above
( well, mine is for a triple dependent drop down menu of countries, states, cities table )
This is for the Controller part,
public function actionDynamicstates() { $data = Worldareasstates::model()->findAll('CountryID=:parent_id', array(':parent_id'=>(int) $_POST['Wsmembersdetails']['CountryID'])); $data = CHtml::listData($data,'StateID','StateName'); foreach($data as $id => $value) { echo CHtml::tag('option',array('value' => $id),CHtml::encode($value),true); } } public function actionDynamiccities() { $data = Worldareascities::model()->findAll('StateID=:parent_id', array(':parent_id'=>(int) $_POST['Wsmembersdetails']['StateID'])); $data = CHtml::listData($data,'CityID','CityName'); foreach($data as $id => $value) { echo CHtml::tag('option',array('value' => $id),CHtml::encode($value),true); } }
here's the "_form" view code
<div class="row"> <?php echo $form->labelEx($model,'Country'); ?> <?php $country = new CDbCriteria; $country->order = 'CountryName ASC'; ?> <?php echo $form->dropDownList($model,'CountryID',CHtml::listData(Worldareascountries::model()->findAll($country),'CountryID','CountryName'), array( 'ajax' => array( 'type' => 'POST', 'url' => CController::createUrl('wsmembersdetails/dynamicstates'), 'update' => '#Wsmembersdetails_'.StateID ) ) ); ?> <?php echo $form->error($model,'CountryID'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'State'); ?> <?php $state = new CDbCriteria; $state->order = 'StateName ASC'; ?> <?php echo $form->dropDownList($model,'StateID',CHtml::listData(Worldareasstates::model()->findAll($state),'StateID','StateName'), array( 'ajax' => array( 'type' => 'POST', 'url' => CController::createUrl('wsmembersdetails/dynamiccities'), 'update' => '#Wsmembersdetails_'.CityID ) ) ); ?> <?php echo $form->error($model,'StateID'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'CityID'); ?> <?php echo $form->dropDownList($model,'CityID',array());?> <?php echo $form->error($model,'CityID'); ?> </div>
I hope that code snippet , can help anyone who'll be encountering same situation, yii yii yii :)
A note about data param
By default, all form elements' values are sent. But we need only "country_id". So you can simply write:
'data'=>array('country_id'=>'js:this.value'),
no need to include controller
you don't need to pass controller name for the same controller:
'url'=>CController::createUrl('currentController/ajaxRegions'), //url to call.
enough is to write:
'url'=>CController::createUrl('ajaxRegions'), //url to call.
city should be pre-filled
Actually, the city should not be empty as in the example:
echo CHtml::dropDownList('city_id','', array());
because when you are editing a record, it will not be filled in.
If you need this to set to the actual data, it should be the standard way:
echo CHtml::dropDownList('city_id',$model->city_id, CHtml::listData(City::model()->findAll(),'id','name'));
Default values
Just for the reference, to properly select 'selected' fields in dependent dropdown, I had to do as following:
<?php echo CHtml::dropDownList('country_id', ($model->isNewRecord) ? '' : $model->region->country->id, CHtml::listData(Country::model()->findAll(), 'id', 'name'), array( 'prompt' => '', 'ajax' => array( 'type'=>'GET', //request type 'url'=> $this->createUrl('shop/ajaxregions'), 'update'=>'#Shop_region_id', 'data'=>array('country_id'=>'js:this.value'), ))); ?> <?php echo $form->dropDownList($model, 'region_id', ($model->isNewRecord) ? array() : CHtml::listData(CountryRegion::model()->findAll(), 'id', 'name')); ?>
Not Run without form tag.
some time ago, I tried to practice the way of the above. but i can not get the post.
in addition only that the $ _POST not be sent if not in the Form tag.
and I added this code:
<?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'account-form', 'enableAjaxValidation'=>true, 'enableClientValidation'=>true, 'focus'=>array($model1,'kdakun'), )); ?> <? echo CHtml::dropDownList('country_id','', array(1=>'USA',2=>'France',3=>'Japan'), array( 'ajax' => array( 'type'=>'POST', //request type 'url'=>CController::createUrl('gl/test'), //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()); ?> <?php $this->endWidget(); ?>
controller:
public function actionTest(){ $tag = $_POST['country_id']; echo CHtml::tag('option',array('value'=>'A' ),CHtml::encode('A - ' . $tag),true); }
hope may be useful.
Updating several dropdowns
Thank you for a nice tutorial! To update several dropdowns based on one dropdown value, we can use this:
In the view:
'ajax'=>array( 'type'=>'POST', 'dataType'=>'json', 'data'=>array('color'=>'js: $(this).val()'), 'url'=>CController::createUrl('material/getGraniteOptions'), 'success'=>'function(data) { $("#dropdownA").html(data.dropdownA); $("#dropdownB").html(data.dropdownB); }', )
In the controller:
// fetch your data first ..... foreach($dataA as $value=>$name) $dropDownA .= CHtml::tag('option', array('value'=>$value),CHtml::encode($name),true); foreach($dataB as $value=>$name) $dropDownB .= CHtml::tag('option', array('value'=>$value),CHtml::encode($name),true); // return data (JSON formatted) echo CJSON::encode(array( 'dropDownA'=>$dropDownA, 'dropDownB'=>$dropDownB ));
Hope this may come handy to someone else.
Full example for Provinces/Cities/Districts
Just to give a full concrete example using 3 dependant DropDownList for Provinces/Cities/District. This Wiki was very useful, and so was re1nald0 post! Thanks
View:
echo CHtml::dropDownList('idProvince','', $provinces, array( 'prompt'=>'Select Province', 'ajax' => array( 'type'=>'POST', 'url'=>CController::createUrl('User/updateCities'), 'dataType'=>'json', 'data'=>array('idProvince'=>'js:this.value'), 'success'=>'function(data) { $("#idCity").html(data.dropDownCities); $("#idDistrict").html(data.dropDownDistricts); }', ))); echo CHtml::dropDownList('idCity','', array(), array( 'prompt'=>'Select City', 'ajax' => array( 'type'=>'POST', 'url'=>CController::createUrl('User/updateDistricts'), 'update'=>'#idDistrict', 'data'=>array('idCity'=>'js:this.value'), ))); echo CHtml::dropDownList('idDistrict','', array(), array('prompt'=>'Select District'));
And Controller:
public function actionUpdateCities() { //Cities $data = City::model()->findAll('idProvince=:idProvince', array(':idProvince'=>(int) $_POST['idProvince'])); $data = CHtml::listData($data,'idCity','name'); $dropDownCities = "<option value=''>Select City</option>"; foreach($data as $value=>$name) $dropDownCities .= CHtml::tag('option', array('value'=>$value),CHtml::encode($name),true); //District $dropDownDistricts = "<option value='null'>Select District</option>"; // return data (JSON formatted) echo CJSON::encode(array( 'dropDownCities'=>$dropDownCities, 'dropDownDistricts'=>$dropDownDistricts )); } public function actionUpdateDistricts() { $data = District::model()->findAll('idCity=:idCity', array(':idCity'=>(int) $_POST['idCity'])); $data = CHtml::listData($data,'idDistrict','name'); echo "<option value=''>Select District</option>"; foreach($data as $value=>$name) echo CHtml::tag('option', array('value'=>$value),CHtml::encode($name),true); }
Filter DropDownList
Excellent post i can do!!!
working with CActiveForms
Following on from ehochedez's and re1naldo's comments relating to multiple dropDownLists...
If you're trying to dynamically generate elements for CActiveForm.dropDownList() instead of CHtml::dropDownList() you may have experienced difficulties using the method outlined in the tutorial. You need to use CHtml::activeId() to correctly location the CActiveForm.dropDownList().
echo CHtml::dropDownList('idProvince','', $provinces, array( 'prompt'=>'Select Province', 'ajax' => array( 'type'=>'POST', 'url'=>CController::createUrl('User/updateCities'), 'dataType'=>'json', 'data'=>array('idProvince'=>'js:this.value'), 'success'=>'function(data) { $("#idCity").html(data.dropDownCities); $("#'.CHtml.activeId($model, "idDistrict").'").html(data.dropDownDistricts); }', ))); echo CHtml::dropDownList('idCity','', array(), array( 'prompt'=>'Select City', 'ajax' => array( 'type'=>'POST', 'url'=>CController::createUrl('User/updateDistricts'), 'update'=> ".CHtml.activeId($model, 'idDistrict').", 'data'=>array('idCity'=>'js:this.value'), ))); <?php echo $form->labelEx($model, 'idDistrict'); ?> <?php echo $form->dropDownList($model, 'idDistrict', array(), array('prompt'=>'Select District')); ?> <?php echo $form->error($model, 'idDistrict'); ?>
great tutorial
Thanks for this useful exercise!
THANKS
its very useful
work without $_POST['currentController']
Mine is working by dropping ['current-Controller']
So, I change this..
$data=Location::model()->findAll('parent_id=:parent_id', array(':parent_id'=>(int) $_POST['Current-Controller']['country_id']));
into this..
$data=Location::model()->findAll('parent_id=:parent_id', array(':parent_id'=>(int) $_POST['country_id']));
Why is this happening?
Dropdown is not active when error came
I have a form with three field name test box two dropdown box, dropdown is working when i choose country dropdown it displayed city, But if i leave a name field then submit it display error, That time city dropdown is not active, Help me
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.
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
4 daimention list box generate in yii
view:
<?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'ram-city-form', 'enableAjaxValidation'=>false, )); ?><?php
/ @var $this RamCityController /
/ @var $model RamCity /
/ @var $form CActiveForm /
?>
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); } }
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
$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 "   "; // 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?
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') ); ?>
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
Updates in text box
For updating textbox value based on drop down menu selection, what changes needed in form and controller action?
thanks.
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
Three Layer Dependent Dropdownlist
Is there anyway I can make three layer dependent dropdownlist?
I've tried but no luck.
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
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
@wesh ask in forum
Hi there, I suggest you ask your question in Yii forum, you get answer more faster.
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
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
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.
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.
2 sercio
http://www.yiiframework.com/wiki/275/how-to-write-secure-yii-applications/
set 1 textfield from 2 dropdownlist selected value
could you help me for this:
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...
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']
Using CActiveForm with a Model
Thanx Mahdi Miad, I got that working.
Just a change I had to make to the controller code:
array(':parent_id'=>(int) $_POST['currentController'] ['country_id']));));
must be
array(':parent_id'=>(int) $_POST['currentModel'] ['country_id']));
Regards
Multiple Ajax calls
For some reason this code is resulting in multiple ajax requests (I can see 6 of them in Firebug when I make a selection from my dropdown list). What am I doing wrong here?
View:
<?php echo $form->dropDownListRow($plan,'plan_id', CHtml::listData($this->plans, 'id', 'name'), array( 'empty'=>'--Select a Plan--', '', 'ajax' => array( 'type'=>'POST', 'url'=>$planDetailUrl, 'data'=> "js:$('#plan-form').serialize()", 'dataType'=>'json', 'beforeSend'=>"function() { $('div#planForm').addClass('loading'); }", 'success'=>"function(data) { if (data.status == 'success') { if(data.feeAmount != null) { setFeeAmount(data.feeAmount); } if(data.billInterval=='Monthly') { $('label[for=\"pymt_day_of_month\"]').css('display',''); $('#pymt_day_of_month').css('display',''); } $('div#planForm').removeClass('loading'); }", ))); ?>
Controller:
public function actionPlandetails() { $id = $_POST['UserPlan']['plan_id']; $plan = Plan::model()->findByPk($id); $fee = $plan == null ? 0.00 : $plan->fee; $billInterval = $plan->billInterval == null ? '' : $plan->billInterval->name; if(Yii::app()->request->getIsAjaxRequest()) { echo CJSON::encode(array( 'status' => 'success', 'feeAmount' => !strrchr($fee,'.') ? $fee.'.00' : $fee, 'billInterval' => $billInterval, ) ); Yii::app()->end(); } }
error
I got this error help me out
Invalid argument supplied for foreach()..
Error - Reply
Invalid argument supplied for foreach()..
@Vijay, foreach() accepts an array to begin with so if your argument is not an array, it throws that warning.
Alternatively check if the arg passed is array before calling foreach
if (is_array($arrayToSend) ){ foreach ($arrayToSend as $value ){ .. } }
That been said, it would be helpful if you post your code with your questions so you can get adequate help from members.
Thank you.
Code
@amazing..
this s my code on controller i got that "Invalid argument supplied for foreach().."
public function actionDynamiccities() { $sql = "SELECT * FROM city ". "WHERE state_id = :state"; $command = Yii::app()->createCommand($sql); $command->bindValue(':state', $_POST['Address']['state']); $data = $command->execute(); /* $data=City::model()->findAll('state_id=:state_id', array(':state_id'=>(int) $_POST ['Address']['state']));*/ $data=CHtml::listData($data,'city_id','city'); foreach($data as $value=>$name) { echo CHtml::tag('option', array('value'=>$value),CHtml::encode($name),true); } }
I got error error in this line. if i comment this line in my _view. i didnt get error and
i didnt got result..
echo $form->dropDownList($model,'city','',array());
Try
@Vijay P S
call queryAll instead execute
try:
$sql = "SELECT * FROM city WHERE state_id = :state"; $rows = $command = Yii::app()->createCommand($sql)->queryAll(true, array('state', $_POST['Address']['state'])); foreach($rows as $row) { echo CHtml::tag('option', array('value'=>$row['city_id'], 'encode' => true), $row['city']); }
Error Fix @Vijay
Hello @Vijay,
Thank you for your feedback,
From your code...even if you use queryAll, it might not work as expected because CHtml::listData() accepts models but queryAll() will return associative arrays.
Besides queryAll() is a method stemming from Yii::app()->db instance and hence
Yii::app()->db->createCommand($sql)->queryAll();
$command = Yii::app()->db->createCommand($sql)->queryAll(true, array('state', $_POST['Address']['state']));
Hence try the code below and let me know the outcome assuming you have a city model class.
public function actionDynamiccities() { $stateCode = isset($_POST['Address']['state']) ? $_POST['Address']['state'] : ''; $criteria = new CDbCriteria(); $criteria->select = array('city_id','city'); $criteria->condition = 'state_id=:state'; $criteria->params = array(':state'=> $stateCode); // $criteria->order = 'city ASC'; uncomment to order the list $cities = City::model()->findAll($criteria); if (is_array($cities) ) { foreach($cities as $value=>$name) { echo CHtml::tag('option', array('value'=>''.$value),CHtml::encode($name),true); } } else { echo CHtml::tag('option', array('value'=>''.'0'),CHtml::encode('No cities found'),true); } }
@Vijay Error Fix
@Vijay, Did our suggestions worked? Any feedback...
About Error
@amazing
the problem is in the POST. Empty value is send to the controller.
In the browser console it shows like this when i execute the ddl.
POST http://192.168.1.13/fdms/index.php?r=Subheads/dynamicCategory 403 (Forbidden)
How can i resolve this????
&thank you for ur support amazing.
@ Error FIX
Atlast I cleared my error.
The problem is in the access rule. when we add a new function in the controller we need to add the function name in the access rule and we need to specify the authenticate for user or for the admin.. that's y i got error in POST the value to the controller.
My new function in my controller is dynamicCategory().
array('allow', // allow admin user to perform 'admin' and 'delete' actions 'actions'=>array('admin','delete','DynamicCategory'), 'users'=>array('admin'), ),
PLZ read the article carefully line by line before u start working.
@amazing thank you for your endless support and for the yiiteam.
Slow Response
Good Day!
I have problem in using the dependent dropdown specially on its response time. It takes selecting many options in the dropdown for it to respond. Thanks for any feedback.
God Bless!
Slow response @pjravs
@pjravs, please could you explain further the exact problem with codes as well so you can get someone to help.
What I am not getting is whether it takes a long time for the second dropdown to receive data after selection in the first dropdown
or
It takes having to make several selections in the first dropdown for it to work.
Thanks
Response Time
Good Day!
@Amazing Thanks for the feedback. I have used firebug and noticed that the request upon the server side takes about 3 to 4 seconds for the dropdown to be filled up with options. The code was identical to the example above.
Response Time @pjravs
@pjravs without seeing what you are doing even if the code is similar, it would be hard to pinpoint what the exact issue is. It could be your queries are not optimized or there are no indexes on the searched column in the database (dealing with large datasets) or something else.
Kindly share your code and lets see what's going on.
Thanks
WHen I change the option in dropdown the ajax call is going to site/index not to controller i defined
When I change the option in dropdown the ajax call is going to site/index not to controller i defined. Below is the code of view and controller, I am using default siteController.php, it doesn't have any access code anywhere, I dont see any access array in the controller.
View code
<div class="row"> <?php echo $form->labelEx($model,'city'); ?> <?php echo $form->dropDownList($model,'city', CHtml::listData(Connecttbl::model()->findAll(array('order' => 'city ASC')), 'id', 'city'), array('empty'=>'Select city'), array( 'ajax' => array( 'type' => 'POST', 'url' => CController::createUrl('getstreets'), 'data' => array('category', 'js:this.value'), 'update' => '#'.CHtml::activeId($model, 'streets') ))); ?> <?php echo $form->error($model, 'city'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'streets'); ?> <?php echo CHtml::dropDownList('streets','', array()); ?> <?php echo $form->error($model,'streets'); ?> </div>
Controller
public function actiongetstreets() { $data = Connecttbl::model()->findAll('id=:id', array(':parentid'=>(int)$_POST['testtbl[city]'])); $data = CHtml::listData($data, 'id', 'street'); foreach($data AS $value=>$name) { echo CHtml::tag('option', array('value'=>$value), CHtml::encode($name),true); } }
NOTE: Fix onUpdate
Nice tutorial. I just want to add something: if you want to update a record, the cities dropDownList will not select the value given onCreate. To fix it you should do:
<?php $cities=array(); if(isset($model->city_id)){ $country = intval($model->country_id); $list=City::model()->findAll("country_id='$country'"); $cities = CHtml::listData($list,'city_id','city_name'); } echo $form->dropDownList($model,'city_id',$cities,array('prompt'=>'Select')); ?>
www.iSystems.am Official project example. We have relations and exclude systems
//in controller public function actionDynamiccarservicetypedetails() { $data=Carservicetypedetail::model()->findAll('carservicetype_id=:carservicetype_id', array(':carservicetype_id'=>(int) $_POST['carservicetype_id'])); $datas=CHtml::listData($data,'id','slug'); $dataii=CHtml::listData($data,'id','id'); $criteria = array( 'select'=>'*', 'condition'=>'`user_id`=:user_id', 'params'=>array(':user_id' => Yii::app()->user->id) ); $dataCs = Carservice::model()->findAll($criteria); $dataCsii=CHtml::listData($dataCs,'carservicetypedetail_id','carservicetypedetail_id'); $dataii = array_diff($dataii, $dataCsii);//exclude iSystems foreach($dataii as $value=>$name) { echo CHtml::tag('option', array('value'=>$value),CHtml::encode($datas[$value]),true); } }
//in model public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'carservicetypedetail' => array(self::BELONGS_TO, 'Carservicetypedetail', 'carservicetypedetail_id'), 'user' => array(self::BELONGS_TO, 'User', 'user_id'), ); }
Best Regards www.iSystems.am Official
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.