转自:
应用中经常遇到需要在表单form中使用两个关联的下拉列表dropdown。其中一个下拉列表中的数据依赖于另一个下拉列表的当前值。例如填写地 址,当 我们修改’国家’下拉列表的值,’城市’列表中的项目就会全部更新为该国的城市。通过Yii内建的AJAX功能可以比较容易的实现关联下拉列表的功能。下 面简要介绍实现过程。
首先,在表单的view文件中,我们需要在表单中显示国家和依赖于国家的城市两个下拉列表。
echo CHtml::dropDownList('country_id','', array(1=>'USA',2=>'France',3=>'Japan'), array( 'ajax' => array( 'type'=>'POST', //url请求类型 'url'=>CController::createUrl('currentController/dynamiccities'), //要调用的url. //Style: CController::createUrl('currentController/methodToCall') 'update'=>'#city_id', //ajax请求要更新的目的元素,这里是城市下拉列表 //'data'=>'js:javascript statement' //这里不设置data,默认将传递当前表单form中的所有元素数据 ))); //此处数据为空,因需要根据其他下拉列表的值来更新 echo CHtml::dropDownList('city_id','', array());
简单起见,在这里第一个’国家’的下拉列表由一系列 键值/文本 数组构成,当然我们也可以简单修改,使其从数据库中读取,并使用CHtml::listData处理成这种key/value数组。
在上面的表单中,一旦它的值发生变化,都会触发一个ajax请求,发送给当前控制器controller的 ‘dynamiccities’动作。请求的结果( ‘dynamiccities’动作的输出)将被更新到第二个下拉列表的值(id是city_id)。
接下来,要修改控制器controller代码,添加’dynamiccities’的动作响应,输出将用来填充‘城市’下拉列表的html。在这里,他需要根据请求的国家代码country_id输出相应的城市列表:
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); } }
这里Location是用来储存城市信 息的AR模型,关联的国家代码字段为parent_id。CHtml::listData将搜索到的数据对象(object)转换为 [key]:value形式的数组,key为属性id的值,value为属性name的值。然后,以tag的形式逐一输出,供第二个下拉列表(城市 dropdown)更新数据。
你可能会奇怪_POST['country_id']参数是哪里来的?其实,当ajax参数数组中‘data’内容为空时(请查看第一个国家列表的ajax设置),ajax会将下拉列表所在表单的所有元素值都通过ajax请求传递给控制器。
想修改也可以。默认情况下,ajax参数数组中的’data’设置为 js:jQuery(this).parents(“form”).serialize() 。前缀js告诉Yii:接下来将是一个javascript语句。所以,如果你想要改变’data’参数的值,你可以加入自己的javascript语 句,并使用js:开头。上述方法对于’success’参数也适用。
还需要修改控制器的规则,给新添加的动作分配适当的权限。例如我们可以把
array('allow', // allow authenticated user to perform 'create' and 'update' actions 'actions'=>array('create','update'), 'users'=>array('@'), ),
修改为
array('allow', // allow authenticated user to perform 'create' and 'update' actions 'actions'=>array('create','update','dynamiccities'), 'users'=>array('@'), ),
小贴士:出于测试目的我们还可以在filters方法中注释掉下面这句:
'accessControl', // perform access control for CRUD operations
这将禁用所有的访问权限控制。测试完成后,别忘了启用它。