CListView sorter as Dropdown rather than links

Hey folks,

if you use a CListView and have too many sortable attributes, a Dropdown-List is much more user-friendly.

This can be "easy" achieved by extending CListView like in my little dirty hack here:




Yii::import('zii.widgets.CListView');


class CustomListView extends CListView

{

	public function renderSorter()

	{

		if($this->dataProvider->getItemCount()<=0)

return;


		

		$link = Yii::app()->controller->createUrl('');

		$modelname = $this->dataProvider->modelClass;


		$attributes = array();

		foreach($this->sortableAttributes as $name=>$label)

			$attributes[$label] = $this->dataProvider->model->getAttributeLabel($label);

		echo '<div style="float: right;">';

		echo 'Sort by: ';

		if(isset($_GET[$modelname.'_sort'])) {

			$selected = explode('.', $_GET[$modelname.'_sort']);

			$selected = $selected[0];

		}

		echo CHtml::dropDownList('sorter',

				isset($_GET[$modelname.'_sort']) 

				? $selected : 0, $attributes);

		echo CHtml::dropDownList('direction', isset($_GET['direction']) ? $_GET['direction'] : 'asc',

				array('asc' => 'Ascending', 'desc' => 'Descending'));

		echo '</div><div style="clear: both;"></div>';


		Yii::app()->clientScript->registerScript('sorter', "

				$('#sorter').change(function() {

					window.location.href = '".$link."?direction='+$('#direction').val()+'&".$modelname."_sort='+$(this).val() + '.' + $('#direction').val();

			

				$('#direction').change(function() {

					window.location.href = '".$link."?direction='+$('#direction').val()+'&".$modelname."_sort='+$('#sorter').val()+'.'+$(this).val();

					});

				");

	}


}		});




It is not perfectly elegant, but it works (for me). Let me know if there is an extension or something similar that solves this in a nicer way, or if you have any ideas on how to make this better.

Thanks - and enjoy!

:lol:

thanks for sharing , have some promotion room : ajax functionality

and expose some things as public variables :

echo ‘<div style=“float: right;”>’; ===> ‘sorterContainerHtmlOptions’ => array(‘style’=>‘float:right’…);

echo 'Sort by: '; ===> echo $this->sortPrefixLable ;// or i18n

I have another approach, at least its little bit elegant for me. :D


<?php

Yii::import('zii.widgets.CListView');

class ListView extends CListView

{

	public $sortCriteria = array();


	/**

	 * Renders the sorter.

	 */

	public function renderSorter()

	{

		$controller	=	$this->getController();

		$url	=	$controller->createUrl($controller->getRoute());

		

		if($this->dataProvider->getItemCount()<=0 || !$this->enableSorting)

			return;

		echo CHtml::openTag('div',array('class'=>$this->sorterCssClass))."\n";

		echo $this->sorterHeader===null ? Yii::t('zii','Sort by: ') : $this->sorterHeader;

		echo CHtml::dropDownList(null,null,array(''=>'Select')+$this->sortCriteria,array(

									'id'=>'sortBy',

									'onchange'=>"$.fn.yiiListView.update('yw0',{ url:'".$url."?".ucfirst($controller->id)."_sort='+$('#sortBy').val()})"));

		

		echo $this->sorterFooter;

		echo CHtml::closeTag('div');

	}

}


?>

then provide some thing like this in your view


<?php

	$this->widget('ListView', array(

		.....

		'sortCriteria'=>array(

				'sold_date.desc'=>'Latest Sold',

				'sold_date'=>'First Sold')

	));

?>

Thanks for sharing, looks very neat.

Thank you very much for this ibiz4

It helped me out a lot.

I had made a change to get it to work properly.

My scenario

Controller: FeedbackController

Model: Referral

Problem

Although it would attempt to perform the sort on the CActiveDataProvider it would never update.

The issue boils down to attempting to use Feedback_sort in the url. This is wrong for my Provider. It should be Referral_sort

Solution

I changed this code:


'onchange'=>"$.fn.yiiListView.update('yw0',{ url:'".$url."?".ucfirst($controller->id)."_sort='+$('#sortBy').val()})"));

To this:


'onchange'=>"$.fn.yiiListView.update('yw0',{ url:'".$url."?".ucfirst($this->dataProvider->modelClass)."_sort='+$('#sortBy').val()})"));

I hope that helps anyone that comes across the same problem.

To add to ibiz4’s answer, if you want the drop down list to stick on the selected value after the list has updated, just add the ‘complete’ option to the yiiListView.update JS:




    echo CHtml::dropDownList(null,null,array(''=>'Select')+$this->sortCriteria,array(

      'id'=>'sortBy',

      'onchange'=>"

        var filterVal = $('#sortBy').val();

        $.fn.yiiListView.update('yw0',{

          url:'".$url."?".ucfirst($controller->id)."_sort='+filterVal,

          complete: function(){ $('#sortBy').val(filterVal); }

        })")

    );



After attempting to add a ‘fliter as you type’ search filter using the above method, I have decided against the approach for the following reasons:

  • You do not get access to the $model object

  • The filter fields refresh (and empty) when the list view is updated

  • A combination of sort and filter cannot be used

Instead, I have opted for a simpler option that provides a little more flexibility, but could be improved…

My solution is to create a filter form directly above the listview, using the same yiiListView.update JavaScript method to reload the list view:




<?php $form=$this->beginWidget('CActiveForm', array(

    'id'=>'filter-form',

)); ?>


  <!-- Sort by dropdown list -->

  <?php echo CHtml::label('Sort by: ', 'Campaign_sort'); ?>

  <?php echo CHtml::dropDownList('Campaign_sort','',array(

    // Enter the sort options here

    'title' => 'Campaign Name',

    'start_date.desc' => 'Newest Campaigns First',

    'start_date' => 'Oldest Campaigns First',

  ),array(

    'prompt' => 'Select',

    'onchange'=>"

      $.fn.yiiListView.update('yw0',{

        data: $('#filter-form').serialize()

      })")

  ); ?>


  <!-- Find as you type filter for the 'title' attribute -->

  <?php echo $form->textField($model,'title',array(

    'onkeyup' => "

      $.fn.yiiListView.update('yw0', {

        data: $('#filter-form').serialize()

      });

    ")

  ); ?>


<?php $this->endWidget(); ?>

<?php

$this->widget('zii.widgets.CListView', array(

  'dataProvider'=>$model->search(),

  'itemView'=>'_listCampaign',

));

?>



Can anyone think of a neater way of integrating a filter form like this without having to explicitly generate it in the view alongside the list view?