Inline editing in Magento backend grids

Backend grids in Magento are very convenient, featuring filtering and pagination using AJAX by default. However, when you want to edit some field or record, you have to create a form. I’d like to describe the way of implementing inline field editing for a field.
A text field renderer class Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Input has already implemented in Magento which allows us to have field’s value in the text input. Lets extend it by adding update button after the input element. For this you need to create Module_NameSpace_Block_Adminhtml_Widget_Grid_Column_Renderer_Inline class extending the original class and override its render() method:

class Module_NameSpace_Block_Adminhtml_Widget_Grid_Column_Renderer_Inline
    extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Input
{
    public function render(Varien_Object $row)
    {
        $html = parent::render($row);

        $html .= '<button onclick="updateField(this, '. $row->getId() .'); return false">' . Mage::helper('modulename')->__('Update') . '</button>';

        return $html;
    }

}

To use the new renderer specify it in addColumn() renderer property:

$this->addColumn('title', array(
    'header'           => Mage::helper('modulename')->__('Title'),
    'align'            => 'center',
    'renderer'         => 'modulename/adminhtml_widget_grid_column_renderer_inline',
    'index'            => 'title',
));

where “modulename” is block group name defined in global/blocks node of config.xml of the module. If everything is correct you will have your title field rendered like this:

Screenshot with rendered title

Time to add some JavaScript to the mix. There are different ways to do it, for example you can add external .js file to the <head> of the document, however I find it more easily to be done by using a block template, because there you can use getUrl() method which generates correct backend URLs relative to backend’s base URL with secret key added.

Next step you need to do is making the system aware that your module uses layouts in the backend. For this specify following nodes in config.xml:

<adminhtml>
    <layout>
        <updates>
            <module_namespace>
                <file>namespace_module.xml</file>
            </module_namespace>
        </updates>
    </layout>
</adminhtml>

and put your layout file into app/design/adminhtml/default/default/layout/namespace/ folder with content:

<?xml version="1.0"?>
<layout>
    <adminhtml_modulename_index>
        <reference name="js">
            <block type="core/template" template="namespace/modulename/inline-edit.phtml" />
        </reference>
    </adminhtml_modulename_index>
</layout>

By using <reference name=”js”> we are adding a new block to <block type=”core/text_list” name=”js” as=”js”/> block defined in main.xml layout of the backend. And because it has type of core/text_list it will render all of its child blocks automatically.

Finally lets create template file with our JavaScript logic:

<script type="text/javascript">
function updateTitle(button, fieldId)
{
    new Ajax.Request('<?php echo Mage::helper('adminhtml')->getUrl('*/*/updateTitle') ?>', {
        method: 'post',
        parameters: { id: fieldId, title: $(button).previous('input').getValue() }
    });
}
</script>

and implement updateTitle() method in your backend controller which will handle saving field’s data:

public function updateTitleAction()
{
    $fieldId = (int) $this->getRequest()->getParam('id');
    $title = $this->getRequest()->getParam('title');
    if ($fieldId) {
        $model = Mage::getModel('modulename/model')->load($fieldId);
        $model->setTitle($title);
        $model->save();
    }
}

That’s it. Text entered into the field will be saved without page reload
I hope this example will help to enhance your backend module for cases when data should be updated quickly. Please share your feedback or questions in the comments below!