Yii custom update for GridView widget

Hooking into the ajax functions in Yii’s GridView and ListView widgets can be a bit tricky and the documentation isn’t always that clear. To do a simple custom filter, you can use something like this:

<?php echo CHtml::dropDownList('Holidays[date]', $model->date, 
  array(
    '2011'=>2011,
    '2012'=>2012,
  ), array('onchange'=>"$.fn.yiiGridView.update('holidays-grid', {data: $(this).serialize()});") ); ?>

Where “Holiday” is the name of the model used to populate the widget and “holidays-grid” is the html ID of the widget. (In real code I would fetch the drop-down values dynamically, but this shows how simple it can be.)

One somewhat undocumented issue is that you must have a particular URL manager rule set or you will get unpredictable results when combining this with sorting and paging:

see: www.yiiframework.com/…/19738-filter-after-sorting-problem-at-gridview/
According to Qiang, “for gridview to work properly in ajax mode, you have to add a URL rule like the following if you use “path” format:”

<controller:\w+>/<action:\w+>' => '<controller>/<action>

This will put all filter conditions into the query string part instead of the pathinfo part.

I spent a bit of time coming up with regex work-around until I found that :/

Yii admin form drop-downs – with Ajax and other examples

Yii’s admin forms are great way to get a quick start on a project that requires admin tools. It would be great if there was some way to automatically create drop-downs for related tables, especially if they already have foreign keys. Perhaps someone has made an extension, but I haven’t found one.

In any case it’s pretty easy to add a drop down, below are some examples, including an Ajax form that updates another value via Ajax onchange.

Yii drop-down with custom sort:

<div class="row">
  <?php echo $form->labelEx($model,'table_name_id'); ?>
  <?php echo $form->dropDownList($model, 'table_name_id', 
    CHtml::listData(table_name::model()->findAll(
      array('order'=>'t.name_admin ASC')
    ), 'id', 'name_admin'),
    array('prompt'=>'Select Name...')
  ); ?>
  <?php echo CHtml::error($model,'table_name_id'); ?>
</div>

with ajax call onchange:
this example changes the entire contents of one drop-down (feature category) based on the language selected:

<div class="row">
  <?php echo $form->labelEx($model,'language_id'); ?>
  <?php echo $form->dropDownList('feature[language_id]', $lang_id,
    CHtml::listData(language::model()->findAll(
      array('select'=>'name, id','condition'=>'active = "y"')
    ), 'id', 'name'),
    array(
      'ajax' => array(
        'type'=>'POST', //request type
        'url'=>$this->createUrl('feature/herdcats'), //url to call
        'update'=>'#feature_category_id', //selector to update
        'data'=>array('feature[language_id]'=>'js:$(\'#feature_language_id\').val()'),
        //'data'=>'js:javascript statement' 
        //leave out the data key to pass all form values through
        // another option to update or replace (will supersede those):
        // 'success'=>' function(data) { $(\'#feature_category_id\').val(data) }',
      )//,'confirm' => 'foo',//, 'onchange' => 'alert("foo2")',
    )
  ); ?>
  <?php echo $form->error($model,'language_id'); ?>
</div>

controller action for above:

	public function actionHerdcats()
	{	
		// ajax function to retrieve categories by country:
		$lang_id = (!empty($_POST['feature']['language_id'])) ? $_POST['feature']['language_id'] : DEFAULT_LANG_ID;
 
		$data = category::model()->findAll('language_id=:parent_id', 
					  array(':parent_id'=>(int) $lang_id));
 
		$data = CHtml::listData($data,'id','name');
		if (!empty($data)) { echo '<option value="">Select Category...</option>'; }
		foreach($data as $value=>$name) {
			echo CHtml::tag('option',
				array('value'=>$value),CHtml::encode($name),true);
		}
	}

manual data drop-down:

<?php echo $form->dropDownList($model, 'status', 
  array(
    '1'=>'active',
    '0'=>'inactive'
  ) ); ?>

check for condition before running action in a controller:

protected function beforeAction($action)
{
  if (parent::beforeAction($action)) {
    //check GET parameters
    if (in_array($action->id, array('update','delete'))) {
      if (empty($_GET['id']))
        throw new CHttpException(404,'Not found');
     }
     return true;
  } else
    return false;
}