Editable

Creates single editable element. Serves mainly as base class for other widgets.

Example

Widget source

<?php
    $this->widget('editable.Editable', array(
        'type'      => 'text',
        'name'      => 'user_name',
        'pk'        => 1,
        'text'      => 'John',
        'url'       => $this->createUrl('site/updateUser'), 
        'title'     => 'Enter user name',
        'placement' => 'right'
    ));
?>

EditableField

Makes editable single attribute of model. Attribute should be safe (e.g. defined in rules() method of model)
It can be one of several types:

1. Text

Example
(try empty value to test validation)

Widget source

<?php
    $this->widget('editable.EditableField', array(
        'type'      => 'text',
        'model'     => $user,
        'attribute' => 'user_name',
        'url'       => $this->createUrl('site/updateUser'), 
        'placement' => 'right',
    ));
?>

In model User.php

public function rules()
{
    return array(
        array('user_name', 'required'), //make user_name safe and required
        ...
    );
}

2. Textarea

Example

Widget source

<?php
    $this->widget('editable.EditableField', array(
        'type'        => 'textarea',
        'model'       => $user,
        'attribute'   => 'user_comment',
        'url'         => $this->createUrl('site/updateUser'), 
        'placement'   => 'right',
        'showbuttons' => 'bottom',
    ));
?>

3. Select (load items from array)

Example

Widget source

<?php
    $this->widget('editable.EditableField', array(
        'type'      => 'select',
        'model'     => $user,
        'attribute' => 'user_status',
        'url'       => $this->createUrl('site/updateUser'), 
        'source'    => CHtml::listData(Status::model()->findAll(), 'status_id', 'status_text'),
        'placement' => 'right',
    ));
?>

4. Select (load items from url)

Example

Widget source

<?php
    $this->widget('editable.EditableField', array(
        'type'      => 'select',
        'model'     => $user,
        'attribute' => 'user_status_ext',
        'url'       => $this->createUrl('site/updateUser'), 
        //since 1.1.0 source must be string for remote loading (not array Yii route!)
        'source'    => $this->createUrl('site/getStatusList'), 
        'placement' => 'right',
    ));
?>

Action site/GetStatusList

public function actionGetStatusList()
{
    echo CJSON::encode(CHtml::listData(Status::model()->findAll(), 'status_id', 'status_text')); 
}

5. Date

Example

Widget source

<?php
    $this->widget('editable.EditableField', array(
        'type'        => 'date',
        'model'       => $user,
        'attribute'   => 'user_dob',
        'url'         => $this->createUrl('site/updateUser'),         
        'placement'   => 'right',
        'viewformat'  => 'dd/mm/yyyy'
    ));
?>

In model rules()

   array('user_dob', 'default', 'value' => null), //make user_dob safe and convert "" to null

i18n:
For Bootstrap datepicker you should include lang file from here and set option language

$this->widget('editable.EditableField', array(
    ...
    'options'     => array(
        'datepicker' => array('language' => 'ru') 
    )   
));
Yii::app()->clientScript->registerScriptFile(Yii::app()->baseUrl.'/js/bootstrap-datepicker.ru.js', CClientScript::POS_END);

For jQuery UI datepicker you just should include lang file from here.

Yii::app()->clientScript->registerScriptFile(Yii::app()->baseUrl.'/js/jquery.ui.datepicker-ru.js', CClientScript::POS_END);

6. Datetime (bootstrap only!)

Example

Widget source

    <?php
    $this->widget('editable.EditableField', array(
        'type'        => 'datetime',
        'model'       => $user,
        'attribute'   => 'user_visit',
        'url'         => $this->createUrl('site/updateUser'),         
        'placement'   => 'right',
        'format'      => 'yyyy-mm-dd hh:ii',
        'viewformat'  => 'dd/mm/yyyy hh:ii',
    ));
?>    

In model rules()

      array('user_visit', 'default', 'value' => null), //make attribute safe and convert "" to null
    

7. Combodate

Example

Widget source

<?php
    $this->widget('editable.EditableField', array(
        'type'        => 'combodate',
        'model'       => $user,
        'attribute'   => 'user_reserve',
        //define value directly as it should match the format 
        'value'       => isset($user->user_visit) ? date('Y-m-d H:i', strtotime($user->user_visit)) : '', 
        'url'         => $this->createUrl('site/updateUser'),
        'placement'   => 'right',      
        'format'      => 'YYYY-MM-DD HH:mm', //in this format date sent to server  
        'viewformat'  => 'MMM DD, YYYY HH:mm', //in this format date is displayed
        'template'    => 'DD / MMM / YYYY HH:mm', //template for dropdowns
        'combodate'   => array('minYear' => 1980, 'maxYear' => 2013), 
    ));
?>

In model rules()

  array('user_reserve', 'default', 'value' => null), //make attribute safe and convert "" to null

8. Checklist

Example

Widget source

<?php
    $this->widget('editable.EditableField', array(
        'type'      => 'checklist',
        'model'     => $user,
        'attribute' => 'user_params',
        'placement' => 'right',
        'url'       => $this->createUrl('site/updateUser'),
        'source'    => array('pretty', 'awesome', 'smart', 'ambitious'), 
    ));
?>

In model User.php

public function rules()
{
    return array(
        array('user_params', 'implodeParams'), //define custom validator to implode checked values into string
        ...
    );
}

public function implodeParams($attribute) 
{
    if(is_array($this->$attribute)) {
        $this->$attribute = implode(',', $this->$attribute);  //in db it is stored as string e.g. '1,3,4'
    }
}

9. Select2

Example

Widget source

<?php
    $tags = array(
      array('id' => 1, 'text' => 'php'),
      array('id' => 2, 'text' => 'html'),
      array('id' => 3, 'text' => 'css'),
      array('id' => 4, 'text' => 'javascript'),
    );
    
    $this->widget('editable.EditableField', array(
        'type'      => 'select2',
        'model'     => $user,
        'attribute' => 'user_tags',
        'url'       => $this->createUrl('site/updateUser'), 
        'source'    => $tags,
        'placement' => 'right',
        'select2'   => array(
           'multiple' => true
        )
    ));
?>

In model rules()

public function rules()
{
    return array(
        array('user_tags', 'implodeParams'), //define custom validator to implode checked values into string
        ...
    );
}
public function implodeParams($attribute) 
{
    if(is_array($this->$attribute)) {
        $this->$attribute = implode(',', $this->$attribute);  //in db it is stored as string e.g. '1,3,4'
    }
}
To learn about controller action for these widgets please see EditableSaver.

EditableDetailView

Makes editable several attributes of single model, shown as name-value table. Just define editable section inside attribute config. Only safe attributes become editable.

1. Existing record

User Nameyiiuser
Group
Status
Date of birth
LanguageFrance
CommentNo comments..
Created2012-09-09 01:26:06

2. New record

User Name
Group
Status
Date of birth
LanguageNot set
Comment
CreatedNot set

Source (equal for both cases)

<?php
    $this->widget('editable.EditableDetailView', array(
    'data'       => $user,
    
    //you can define any default params for child EditableFields 
    'url'        => $this->createUrl('site/updateUser'), //common submit url for all fields
    'params'     => array('YII_CSRF_TOKEN' => Yii::app()->request->csrfToken), //params for all fields
    'emptytext'  => 'no value',
      
    'attributes' => array(
        array(
            'name' => 'user_name',
            'editable' => array(
                'type'       => 'text',
                'inputclass' => 'input-large',
                'emptytext'  => 'special emptytext',                
                'validate'   => 'function(value) {
                    if(!value) return "User Name is required (client side)"
                }'
            )
        ),
        array( //select loaded from database
            'name' => 'group_id',
            'editable' => array(
                'type' => 'select',
                'source' => CHtml::listData(Group::model()->findAll(), 'group_id', 'group_name')
             )
        ),
        array( //select loaded from ajax.
            'name' => 'user_status',
            'editable' => array(
                'type' => 'select',
                'source' => $this->createUrl('site/getStatuses'),
            )
        ),
        array(
            'name' => 'user_dob',
            'editable' => array(
                'type' => 'date',
                'viewformat' => 'dd/mm/yyyy'
            )
        ),
        array( //edit related record
            'name' => 'profile.language',
            'editable' => array(
                'url' => array('site/updateProfile') //related record requires own submit url
            )
        ),        
        'user_comment',
        'created_at', //not editable as attribute is not safe
    )
    ));
?>

For new record you just pass to widget instance of new model, e.g. $user = new User;.

When you click on Save button all values are collected, validated and submitted to server (you can find more details here).
Onclick handler can be set as following:

<? if($user->isNewRecord) {
        Yii::app()->clientScript->registerScript('new-user', '
            $("#save-btn").click(function() {
                $(this).parent().parent().find(".editable").editable("submit", {
                    url: "'.$this->createUrl('site/createUser').'",
                    ajaxOptions: { dataType: "json" },                    
                    success: function(data, config) {
                        if(data && data.id) {
                            $(this).editable("option", "pk", data.id);
                            $(this).removeClass("editable-unsaved");
                            $("#msg").removeClass("alert-error").addClass("alert-success")
                                     .html("User created! Now you can update it.").show();
                            $("#save-btn").hide();
                        } else {
                            config.error.call(this, data && data.errors ? data.errors : "Unknown error");
                        }
                    },
                    error: function(errors) {
                        var msg = "";
                        if(errors && errors.responseText) { 
                            msg = errors.responseText;
                        } else {
                            $.each(errors, function(k, v) { msg += v+"<br>"; });
                        } 
                        $("#msg").removeClass("alert-success").addClass("alert-error")
                                 .html(msg).show();         
                     }
                });
            });
        ');   
    }
?>

Server-side action to create new record:

public function actionCreateUser()
{
    if(Yii::app()->request->isPostRequest) {
        $model = new User;
        $model->attributes = $_POST;
        if($model->save()) {
            echo CJSON::encode(array('id' => $model->primaryKey));
        } else {
            $errors = array_map(function($v){ return join(', ', $v); }, $model->getErrors());
            echo CJSON::encode(array('errors' => $errors));
        }
    } else {
      throw new CHttpException(400, 'Invalid request');  
    }
}

EditableColumn

Makes editable one column in GridView. Grid stays editable after ajax sorting and pagination. Parameters are defined in editable section of column config.
Since 1.1 works with both CActiveDataProvider and CArrayDataProvider.

Example

Source

<? $this->widget('bootstrap.widgets.TbGridView', array(
    'id' => 'user-grid',
    'itemsCssClass' => 'table-bordered',
    'dataProvider' => $dataProvider,
    'columns'=>array(
        array(
           'class' => 'editable.EditableColumn',
           'name' => 'user_name',
           'headerHtmlOptions' => array('style' => 'width: 110px'),
           'editable' => array(    //editable section
                  'url'        => $this->createUrl('site/updateUser'),
                  'placement'  => 'right',
              )               
        ),
        
        array( 
              'class' => 'editable.EditableColumn',
              'name' => 'user_status',
              'headerHtmlOptions' => array('style' => 'width: 100px'),
              'editable' => array(
                  'type'     => 'select',
                  'url'      => $this->createUrl('site/updateUser'),
                  'source'   => $this->createUrl('site/getStatuses'),
                  'options'  => array(    //custom display 
                     'display' => 'js: function(value, sourceData) {
                          var selected = $.grep(sourceData, function(o){ return value == o.value; }),
                              colors = {1: "green", 2: "blue", 3: "red", 4: "gray"};
                          $(this).text(selected[0].text).css("color", colors[value]);    
                      }'
                  ),
                 //onsave event handler 
                 'onSave' => 'js: function(e, params) {
                      console && console.log("saved value: "+params.newValue);
                  }' 
              )
         ),
      
         array( 
              'class' => 'editable.EditableColumn',
              'name' => 'user_dob',
              'headerHtmlOptions' => array('style' => 'width: 100px'),
              'editable' => array(
                  'type'          => 'date',
                  'viewformat'    => 'dd.mm.yyyy',
                  'url'           => $this->createUrl('site/updateUser'),
                  'placement'     => 'right',
              )
         ), 
         
         array( 
            'class' => 'editable.EditableColumn',
            'name' => 'user_comment',
            'editable' => array(
                'type'      => 'textarea',
                'url'       => $this->createUrl('site/updateUser'),
                'placement' => 'left',
            )
          ),   
    ),
)); 
?>

Options

These options are passed as configuration array into EditableField or into editable section of EditableDetailView and EditableColumn.
Please refer to X-editable docs for the most detailed description (options marked with x-editable).

NameTypeDefaultDescriptionRelated
applybooleannullwhether to apply 'editable' js plugin to element. Only safe attributes become editable.Yii
attributestringrequiredattribute name.Yii
cssFilemixed'jquery-ui.css'for jQuery UI only. The theme CSS file name. By default Yii's jquery UI css used.Yii
disabledbooleanfalsewill editable be initially disabled. It means editable plugin will be applied to element, but you should call .editable('enable') method to activate it. To totally disable applying 'editable' to element use apply option.x-editable
emptytextstringnulltext shown on empty field. If null - default X-editable value is used: Emptyx-editable
encodebooleantruewhether to HTML encode text on outputYii
formatstringnullformat to send date on server. If null - default X-editable value is used: yyyy-mm-dd.x-editable
htmlOptionsarrayarray()HTML options of elementYii
inputclassstringnullcss class of input. If null - default X-editable value is used: input-mediumx-editable
modestringnullmode of input: inline | popup. If not set - default X-editable value is used: popup.x-editable
modelCActiveRecordrequiredActiveRecord to be updated.Yii
name*stringnullname of fieldx-editable
optionsarrayarray()all config options of x-editable. See full list here.Yii
paramsarraynulladditional params to send on serverx-editable
pk*mixednullprimary keyx-editable
placementstringnullplacement of popup. Can be left, top, right, bottom. If null - default X-editable value is used: topx-editable
showbuttonsstringnullvisibility of buttons. Can be boolean false|true or string bottom.x-editable
sourcemixednullsource data for select, checklist. Can be string (url) or array in format: array( array("value" => 1, "text" => "abc"), ...)x-editable
text*stringnulltext to be shown as element contentYii
themestring'base'for jQuery UI only. The JUI theme name.Yii
themeUrlstringfor jQuery UI only. The root URL that contains JUI theme folders. If not set, default Yii's theme will be used.Yii
titlestringnulltitle of popup. If null - will be generated automatically from attribute label. Can have token {label} inside that will be replaced with actual attribute label.Yii
typestringnulltype of editable widget. Can be text, textarea, select, date, checklist, etc.x-editable
urlstringnullurl to submit value. Can be string or array containing Yii route, e.g. array('site/updateUser')x-editable
valuemixednullinitial value. If not set - will be taken from textx-editable
viewformatstringnullformat to display date in element. If null - equals to format option.x-editable

* - required only for raw Editable class. Not used for other classes as CModel provides these data automatically.

Callbacks

NameDescriptionRelated
validateA javascript function that will be invoked to validate value.

Example:

'validate' => 'js: function(value) {
    if($.trim(value) == "") return "This field is required";
}'
x-editable
successA javascript function that will be invoked to process successful server response.

Example:

'success' => 'js: function(response, newValue) {
    if(!response.success) return response.msg;
}'
x-editable
displayA javascript function that will be invoked to custom display value.

Example:

'display' => 'js: function(value, sourceData) {
     var escapedValue = $("<div>").text(value).html();
     $(this).html("<b>"+escapedValue+"</b>");
}'
x-editable

Events

Please see also Events tab of X-editable docs.

NameDescriptionRelated
onInitA javascript function that will be invoked when editable element is initializedx-editable
onShownA javascript function that will be invoked when editable form is shown Example:
'onShown' => 'js: function() {
    var $tip = $(this).data("editableContainer").tip();
    $tip.find("input").val("overwriting value of input.");
}'
x-editable
onSaveA javascript function that will be invoked when new value is saved Example:
'onSave' => 'js: function(e, params) {
    alert("Saved value: " + params.newValue);
}'
x-editable
onHiddenA javascript function that will be invoked when editable form is hidden Example:
'onHidden' => 'js: function(e, reason) {
   if(reason === "save" || reason === "cancel") {
       //auto-open next editable
       $(this).closest("tr").next().find(".editable").editable("show");
   }
}'
x-editable
For any questions feel free to open issue on github.
Your feedback / contribution will be appreciated!