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:
- For delete button only, there is deleteConfirmation property (string), which can be used for customizing message being displayed as confirmation of delete operation.
- 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.
- 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:
- 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.
- 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:中文翻译
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:
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}', ), ), ),
),
),
),
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.
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); }', ), ), )); ?>
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!
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.
Thanks!
Thank you so much , it's a great solution! simple and useful.
thanks
this article is very useful thanks to the author
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.
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.
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!
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>\'', ), //... ),
thanks
thanks for this great explanation.
it work fine for me
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.
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',
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' ), ), ), ),
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, ) ) ),
Set Button Images globally
How to set images of edit,delete and view button image globally?
Make a button perform a POST request
If you want to make a button perform a (non AJAX) post request, you can use:
Yii::app()->clientScript->registerCoreScript('yii'); // includes jQuery.yii.submitForm $this->widget('zii.widgets.CGridView', [ '...' 'columns' => [ '...', [ 'class' => 'CButtonColumn', 'template' => '{remind}', 'buttons' => array( 'remind' => array( 'url' => function($data) { return Yii::app()->controller->createUrl('remind', ['id' => $data->id]); }, 'click' => new CJavaScriptExpression('function() { jQuery.yii.submitForm(document.body, $(this).attr("href"), {}); return false; }'), ), ), ], ], ]);
Hi,
i just want my checkBox change is_published on model without entering edit mode,
also, its very hard to find any info related on such topic.
But thanks any way for this article and everybody for your comments, i'll try.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.