Yii 1.1: Using CButtonColumn to customize buttons in CGridView

65 followers

Introduction

CGridView is a one of most flexible widgets in Yii and example its flexibility is CButtonColumn used to build buttons for steering model in each grid row. Here in this how-to we will explain ways user can customize CButtonColumn to flexibly fit it to its needs.

Basic customization

In default look CButtonColumn contains three buttons in this order: {view}, {update} and {delete}. Their meaning and behaviour should be obvious.

The easiest way to customize look and behaviour of them is to use series of CButtonColumn properties, like: updateButtonImageUrl (path to image for update button), updateButtonLabel (label for the update button; not HTML-encoded), updateButtonOptions (HTML options for this button, used in the way as many htmlOptions property for many widgets) and updateButtonUrl (a PHP expresion that is evaluated for button and whose result is used as the URL). Respective properties you'll find for other default buttons.

A few remarks:

  1. For delete button only, there is deleteConfirmation property (string), which can be used for customizing message being displayed as confirmation of delete operation.
  2. In PHP expression used for xxxButtonUrl property, you can use variable $row for the row number (zero-based); $data for the data model for the row and $this for the column object.
  3. If you provide an empty string for xxxButtonImageUrl or set it to false, a textual link will be used instead.

Info: Delete confirmation message (and other textual elements of CButtonColumn or CGridView) can be also customized by creating zii.php file in protected/messages/languageID/ (or even better - copying one from for example yii/messages/de/ and translating or customizing it to own needs). Don't forget to set 'coreMessages'=>array('basePath'=>'protected/messages') in components part of your config.php file for the application to force Yii to look in your own messages folder instead of build-in one inside framework directory.

More flexible customizing

If using above properties for customizing many aspects of many buttons is a bit messy in code or if there is need for more complex customization or introduction of new buttons there is a better, more flexible way - by using template and buttons properties.

You can use template property to change order of build-in buttons or remove some of them like this:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{delete}{update}',
)

In CGridView's buttons column build upon above example there will be no view button and delete and update buttons will be in other than default order (delete first).

You can use the same property to introduce new buttons:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{up}{down}{delete}',
)

For new buttons (and of course - for existing, build-in ones too!) you have to specify look and behaviour. buttons property of CButtonColumn is used for it. This property is an array of buttons id (which names must correspond to the one provided in template property) and each button is another array holding its specific properties.

Here you can use:

'buttonID' => array
(
    'label'=>'...',     //Text label of the button.
    'url'=>'...',       //A PHP expression for generating the URL of the button.
    'imageUrl'=>'...',  //Image URL of the button.
    'options'=>array(), //HTML options for the button tag.
    'click'=>'...',     //A JS function to be invoked when the button is clicked.
    'visible'=>'...',   //A PHP expression for determining whether the button is visible.
)

Please, note: Text in label property is displayed only, if you have a textual link! If you are using images (build-in or own) instead of text links, text hold in this property will be rendered as image's alt parameter. If you want to change text of tooltip, which is displayed when user hovers your image button, you have to edit options property instead and give the text to its title parameter, like this:

'buttonID' => array
(
    'label'=>'Text shown as alt text to image or as label to text link...',
    'options'=>array('title'=>'Text shown as tooltip when user hovers image...'),
)

There are similar remarks for above mentioned properties like the one described in first part of this text:

  1. In PHP expression used for url or visible properties, you can use variable $row for the row number (zero-based) and $data for the data model for the row.
  2. If you provide an empty string for imageUrl or set it to false, a textual link will be used instead.

Finally here is an example of introducing new buttons to CButtonColumn:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{email}{down}{delete}',
    'buttons'=>array
    (
        'email' => array
        (
            'label'=>'Send an e-mail to this user',
            'imageUrl'=>Yii::app()->request->baseUrl.'/images/email.png',
            'url'=>'Yii::app()->createUrl("users/email", array("id"=>$data->id))',
        ),
        'down' => array
        (
            'label'=>'[-]',
            'url'=>'"#"',
            'visible'=>'$data->score > 0',
            'click'=>'function(){alert("Going down!");}',
        ),
    ),
),

Please note, that since jQuery is used here, any function passed to click should be surrounded by proper jQuery function call. That is why, we are using 'click'=>'function(){alert("Going down!");}' instead of simple 'click'=>'alert("Going down!");'.

Above example also shows (email button) how to create a valid URL containing controller and view plus current user ID (or any other data from model for current row). It also explains how to use baseUrl function from CHttpRequest class to set an image for button to be a file stored outside protected folder.

Specific delete confirmation

You may notice that a standard set of views generated for CRUD operations by Gii includes (in view and update views) delete menu item with confirmation. This confirmation text can be easily changed/extended to include some record (model) specific data, like record ID.

This is not so simple in CGridView (CButtonColumn), as deleteConfirmation property is not parsed. However, there is a tricky way to achieve this (thanks to mdomba for providing it!) using jQuery. Here is an example:

array
(
        'class'=>'CButtonColumn',
        'deleteConfirmation'=>"js:'Record with ID '+$(this).parent().parent().children(':first-child').text()+' will be deleted! Continue?'",
),

We can also use jQuery's ntn-child function for retrieve contents of other column:

array
(
        'class'=>'CButtonColumn',
        'deleteConfirmation'=>"js:'Do you really want to delete record with ID '+$(this).parent().parent().children(':nth-child(2)').text()+'?'",
),

The jQuery function looks really tricky freeky at first sight. If you wish to know, why it has to be in that form, please read this forum post.

Final words

I hope this short how-to will help in better understanding of how flexible buttons in CGridView can be customized. This is especially important as there are many forum posts asking questions about this. Please, feel free to make any updates or corrections to this article, if you find something is missing or that there are mistakes in it.

Have a nice day and happy Yii-ing! :)

Russian Version: Использование класса CButtonColumn для изменения кнопок в виджете CGridView

Chinese Version:中文翻译

Total 17 comments

#16019 report it
realtebo at 2014/01/10 03:10am
A bettter way to customize buttons with font awesome an tooltip

I'd like to suggest you this way to use font awesome icons, with tooltip.

array(
        'class' => 'zii.widgets.grid.CButtonColumn',
        'htmlOptions' => array('style' => 'white-space: nowrap'),
        'afterDelete' => 'function(link,success,data) { if (success && data) alert(data); }',
        // 'template' => '{plus} {view} {update} {delete}',
        'buttons' => array(
            /*
'plus' => array(
'options' => array('rel' => 'tooltip', 'data-toggle' => 'tooltip', 'title' => Yii::t('app', 'View')),
'label' => '<i class="fa fa-plus"></i>',
'imageUrl' => false,
),
*/
            'view' => array(
                'options' => array('rel' => 'tooltip', 'data-toggle' => 'tooltip', 'title' => Yii::t('app', 'View')),
                'label' => '<i class="fa fa-eye"></i>',
                'imageUrl' => false,
            ),
            'update' => array(
                'options' => array('rel' => 'tooltip', 'data-toggle' => 'tooltip', 'title' => Yii::t('app', 'Update')),
                'label' => '<i class="fa fa-pencil"></i>',
                'imageUrl' => false,
            ),
            'delete' => array(
                'options' => array('rel' => 'tooltip', 'data-toggle' => 'tooltip', 'title' => Yii::t('app', 'Delete')),
                'label' => '<i class="fa fa-times"></i>',
                'imageUrl' => false,
            )
        )
    ),
#14727 report it
rajesh chaurasia at 2013/09/06 05:45am
cbutton column with awesome font button
 array('class'=>'CButtonColumn',
    'template'=>'{update} {view} {delete}',
    'buttons'=>array (
        'update'=> array(
            'label'=>'',
            'imageUrl'=>'',
            'options'=>array( 'class'=>'icon-edit' ),
        ),
        'view'=>array(
            'label'=>'',
            'imageUrl'=>'',
            'options'=>array( 'class'=>'icon-search' ),
        ),
        'delete'=>array(
            'label'=>'',
            'imageUrl'=>'',
            'options'=>array( 'class'=>'icon-remove' ),
        ),
    ),
),
#12892 report it
gobinath at 2013/04/18 07:10am
how to add id here

please tell me a way to id here

'buttons'=>array( 'punchin' => array( 'label'=>'punch in', // text label of the button 'url'=>"CHtml::normalizeUrl(array('punchin', 'id'=>\$data->id))", 'id'=>'punchin',

#12511 report it
jozek; at 2013/03/25 09:21am
Calling javascript functions

Hi,

it is quite nice article. Thanks! I have a question though - how can I call javascript function from the button with parameters, sth like:

...
'options' => array(
  'click' => 'removeCity(this, $data->id, $model->id)',
),

I dont want to put all my javascript code into my view file.

#12486 report it
rajesh chaurasia at 2013/03/23 12:31am
thanks

thanks for this great explanation. it work fine for me

#12215 report it
rackycz at 2013/03/07 04:24am
Custom POST button [SOLVED]

Hi. I was wondering it it was possible to call target actions via POST, not via GET, because I don't want user to see URL with weird parameters like: edit?id=123. To reach this functionality you have to (of course) use FORM with method POST. So you need a way how to add a custom column with custom HTML content to CGridView. Here's my solution:

'columns' => array(
//...
  array( 
        'header'=>'html',
        'type'=>'raw',
        'value'=>'\'
           <form action="'.Yii::app()->createUrl("controller/action").'">
           <input type="hidden" name="id" value="\'.$data->id.\'" />
           <input type="image" src="'.Yii::app()->request->baseUrl.'/img.png" />
           </form>\'', 
        ),
//...
),
#10092 report it
Trejder at 2012/10/04 04:27am
Re: Friendly status for relational delete failure message

@hasanavi: Your example is really pretty and useful. I would only mark out that, your sending HTML formatting (for AJAX) directly from Controller. By doing so, your breaking the idea of MVC and strict separation between code (controller) and styling (view).

I was always told, that in situation like you showed here, your controller should always send out pure message text (or sometimes even only result code) and all the styling should be done directly in the view.

Thanks for a great example!

#10091 report it
Trejder at 2012/10/04 04:24am
Re: Custom POST button

@rackycz: AFAIK you can't sent POST request via URL as it is designed to sent form data.

The only way, that comes to my mind, to solve your problem is to create a form (hidden or not) and use button as just jQuery / JavaScript onClick initiator that will force that form to send itself.

Look in the Internet for any example that uses an image (cool looking button) to send a form. Since image (img tag) itself can't send out form, it uses form's own submit function to send data to server via POST.

Look also into original Yii demo application, to admin section, where you manage users. There is a delete button there. If I remember correct, it uses AJAX to perform (initiate) delete operation. And since that delete operation is done via POST only (due to security reasons), it could also shed some light on your problem.

#10089 report it
rackycz at 2012/10/04 03:13am
Custom POST button

Hi.. Great article, thanks for explanation!

I would really appreciate also an example of adding a custom button that doesn't call target action via URL with GET parameters, but via POST. I googled a lot, tried many things, but nothing worked.

.. I created my own "CGridView" that can create either GET or POST buttons, but it is not as cool as CGridView so I want to learn to work with the original.

PS: GET button = URL + parameters, POST button = FORM.

#8193 report it
Kollipara Rama Krishna at 2012/05/16 08:54am
thanks

this article is very useful thanks to the author

#8124 report it
iGN at 2012/05/11 08:20pm
Thanks!

Thank you so much , it's a great solution! simple and useful.

#6374 report it
willowdan at 2012/01/04 09:10am
Thanks hasanavi

Thanks hasanavi, just used your solution, but modified it a bit, by using the alert function instead of appending a string into an existing html node.

#4121 report it
Trejder at 2011/06/08 02:58am
After delete status message

@hasanavi: I personally think that your example is great and it is worth writing a separate article about it on Yii's Wiki. It deserves a better presentation than just a comment to another article. Please, write one, so other can share your knowledge. And thank you for this example!

#4118 report it
hasanavi at 2011/06/07 09:07pm
Friendly status for relational delete failure message

Many programmer has faced problem in CGridView to show a status message if delete operation fails, specially when it has child record. I've come up with a solution.

In the Controller :

try{
   $this->loadModel($id)->delete();
   Yii::app()->user->setFlash('deleteStatus','Deleted Successfully');
   echo "<div class='flash-success'>Deleted Successfully</div>"; //for ajax
}catch(CDbException $e){
    Yii::app()->user->setFlash('deleteStatus','One or more town is related with this region');
    echo "<div class='flash-error'>One or more town is related with this region</div>"; //for ajax
}
if(!isset($_GET['ajax']))
    $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));

and In the View file

<?php $this->widget('zii.widgets.grid.CGridView', array(
    'id'=>'region-grid',
    'dataProvider'=>$model->search(),
    'filter'=>$model,        
    'columns'=>array(
        'name',
        array(
            'class'=>'CButtonColumn',
                        'afterDelete'=>'function(link,success,data){ if(success) $("#statusMsg").html(data); }',
        ),
    ),
)); ?>
#4025 report it
Trejder at 2011/05/30 06:26am
CButtonColumn and CJuiDialog

@kaliwangansyah: Try this:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{dialog}',
    'buttons'=>array
    (
        'dialog' => array
        (
            'label'=>'[!]',
            'url'=>'"#"',
            'click'=>'function(){$("#dialog_id").dialog("open"); return false;}',
        ),
    ),
),

Written from memory, not tested though. If you find any errors, try to look yourself for answer / solution or on the forum.

#4024 report it
at 2011/05/30 06:18am
Update With CJuidialog

How to use update button for use CJuidialog?

#2788 report it
jeremy at 2011/02/10 10:04am
configuration to make update an Ajax button

it's possible to make the default buttons (e.g. 'update') into Ajax buttons without any code, like this:


...
'columns'=>array(
  ...
  array( 'class'=>'CButtonColumn',
   'buttons'=>array('update'=>
     array(
       'url'=> {some expression to be eval'd for each button}
       'options'=>array(  // this is the 'html' array but we specify the 'ajax' element
         'ajax'=>array(
           'type'=>'POST',
           'url'=>"js:$(this).attr('href')", // ajax post will use 'url' specified above
           'update'=>'{some div to update with ajax response}',
         ),
       ),
     ),
   ),
  ),
),

Leave a comment

Please to leave your comment.

Write new article