Add Beautiful Radio/Checkbox Buttons To Grid

You are viewing revision #4 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version.

next (#5) »

  1. Overview
  2. Requirements
  3. News Model
  4. News Controller
  5. News Views

Overview

This is a tutorial on how to create slick looking status buttons to update a model's status via a grid. Note** This tutorial uses Yii Bootstrap for styling.

The basic idea is to render a grid and, for each row, render a set of radio buttons that asynchronously update their respective model.

Status Grid

Requirements

Yii Bootstrap Yii 1.1.+

News Model

Of course you DB needs a field to hold the status of the news item. This tutorial assumes your field is called status and is a varchar field. Next, We need to add the statuses that a news item can have. For the sake of simplicity, I've added them as constants but you could use a separate DB lookup table.

const STATUS_PUBLISHED = 'published';
    const STATUS_DRAFT = 'draft';
    const STATUS_ARCHIVED = 'archived';

    /**
     * Returns true if the model is published.
     * @return boolean whether the model is published or not
     */
    public function isPublished()
    {
        return ($this->status == self::STATUS_PUBLISHED) ? true : false;
    }

    /**
     * Returns true if the model is a draft.
     * @return boolean whether the model is a draft or not
     */
    public function isDraft()
    {
        return ($this->status == self::STATUS_DRAFT) ? true : false;
    }

    /**
     * Returns true if the model is archived.
     * @return boolean whether the model is archived or not
     */
    public function isArchived()
    {
        return ($this->status == self::STATUS_ARCHIVED) ? true : false;
    }

    /**
     * Returns a well formatted status for the news item.
     * @return string the status of the news item
     */
    public function getStatus()
    {
        return ucwords($this->status);
    }

News Controller

We need to add actions (actionPublish, actionDraft, actionArchive) that will handle the calls to update the status of the news item. I've kept these very simple but you should do some checking before allowing the status of items to be changed - only POST, only AJAX etc.

public function actionPublish($id)
    {
        $model = $this->loadModel($id);
        $model->status = News::STATUS_PUBLISHED;
        $model->save();
    }

    public function actionDraft($id)
    {
        $model = $this->loadModel($id);
        $model->status = News::STATUS_DRAFT;
        $model->save();
    }

    public function actionArchive($id)
    {
        $model = $this->loadModel($id);
        $model->status = News::STATUS_ARCHIVED;
        $model->save();
    }

News Views

The views consist of the normal admin view and a partial view called _status_button. The _status_button view is rendered for each row in the grid. We include a bit a javascript at the bottom of the admin view which will do the actual AJAX calls and disable the button if it is in that state. For example, a published news items should have it's Publish radio button disabled.

Note** This grid uses an anonymous function (closure) to render the partial view. This PHP feature became available in 5.3.0.

admin.php
<h1>Manage News</h1>

<?php
$this->widget('bootstrap.widgets.TbGridView', array(
    'type' => 'striped bordered',
    'id' => 'news-grid',
    'dataProvider' => $model->search(),
    'filter' => $model,
    'columns' => array(
        array(
            'name' => 'title',
            'htmlOptions' => array(
                'class' => 'span10'
            )
        ),
        array(
            'name' => 'status',
            'value' => function($data)
            {
                Yii::app()->controller->renderPartial('_status_button', array(
                    'data' => $data
                ));
            },
            'filter' => array(
                News::STATUS_PUBLISHED => 'Published',
                News::STATUS_ARCHIVED => 'Archived',
                News::STATUS_DRAFT => 'Draft'
            ),
            'htmlOptions' => array(
                'class' => 'span1'
            )
        ),
        array(
            'class' => 'bootstrap.widgets.TbButtonColumn',
        ),
    ),
));
?>

<script type="text/javascript">
    jQuery('#news-grid a.toggleStatus').live('click',function() {

        if ($(this).hasClass('disabled'))
            return false;

        var th=this;

        var afterDelete=function(){};
        $.fn.yiiGridView.update('news-grid', {
            type:'POST',
            url:$(this).attr('href'),
            success:function(data) {
                $.fn.yiiGridView.update('news-grid');
                afterDelete(th,true,data);
            },
            error:function(XHR) {
                return afterDelete(th,false,XHR);
            }
        });

        return false;
    });
</script>
_status_button.php

One important thing to note about the _status_button.php view file is this line:

'class' => ($data->isPublished()) ? 'toggleStatus disabled' : 'toggleStatus',

The bit of javascript at the bottom of admin.php listens to the click event of links with a class of toggleStatus. Therefore, don't forget to change the JS if you change the class of these radio buttons.

<?php
$this->widget('bootstrap.widgets.TbButtonGroup', array(
    'toggle' => 'radio',
    'buttons' => array(
        array(
            'label' => ucwords(News::STATUS_PUBLISHED),
            'type' => ($data->isPublished()) ? 'info' : '',
            'url' => Yii::app()->createUrl('admin/news/publish', array('id' => $data->id)),
            'htmlOptions' => array(
                'id' => 'status' . $data->id,
                'class' => ($data->isPublished()) ? 'toggleStatus disabled' : 'toggleStatus',
            ),
            'active' => ($data->isPublished()) ? true : false,
        ),
        array(
            'label' => ucwords(News::STATUS_ARCHIVED),
            'type' => ($data->isArchived()) ? 'info' : '',
            'url' => Yii::app()->createUrl('admin/news/archive', array('id' => $data->id)),
            'htmlOptions' => array(
                'id' => 'status' . $data->id,
                'class' => ($data->isArchived()) ? 'toggleStatus disabled' : 'toggleStatus',
            ),
            'active' => ($data->isArchived()) ? true : false,
        ),
        array(
            'label' => ucwords(News::STATUS_DRAFT),
            'type' => ($data->isDraft()) ? 'info' : '',
            'url' => Yii::app()->createUrl('admin/news/draft', array('id' => $data->id)),
            'htmlOptions' => array(
                'id' => 'status' . $data->id,
                'class' => ($data->isDraft()) ? 'toggleStatus disabled' : 'toggleStatus',
            ),
            'active' => ($data->isDraft()) ? true : false,
        ),
    )
));
?>

That's it. Hopefully you found this helpful.