Admin Form Grid – How to Insert a Grid with Columns Inside an Admin Form

adminformadminhtmlformsgrid

I need to insert a grid inside the admin form having two columns and each row should be having a delete button. Is there a way to achieve this enter image description here

Best Answer

You need a couple of things. First of all next to the normal table you'll have created for your module that stores the data of the adminhtml form you need a second table that stores the URLs data with the fields

  1. entity_id
  2. parent_id
  3. label
  4. url
  5. sortorder

Create the resource models accordingly.

Rendering in the Adminhtml

Adding a new renderer to the form generator. I've just added the field that will have the renderer

app/code/[codepool]/Namespace/Module/BLock/Adminhtml/[Module]/Edit/Tab/General.php

class [Namespace]_[Module]_Block_Adminhtml_[Module]_Edit_Tab_General extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        $form = new Varien_Data_Form();
        $this->setForm($form);
        $fieldset = $form->addFieldset('[module]_form', array('legend'=>Mage::helper('[namespace]_[module]')->__('General')));

        [...]       

        $urlsField = $fieldset->addField('urls', 'text', array(
            'name'      => 'urls',
            'label'     => Mage::helper('[namespace]_[module]')->__('URLS'),
            'required'  => false,
        ));

    $urlsField = $form->getElement('urls');

    $urlsField->setRenderer(
        $this->getLayout()->createBlock('[namespace]_[module]/adminhtml_[module]_edit_renderer_urls')
    );

    [...]
    }
}

The renderer class that adds the phtml template

app/code/[codepool]/Namespace/Module/BLock/Adminhtml/[Module]/Edit/Renderer/Urls.php

class [Namespace]_[Module]_Block_Adminhtml_[Module]_Edit_Renderer_Urls
 extends Mage_Adminhtml_Block_Widget
 implements Varien_Data_Form_Element_Renderer_Interface
{

    /**
     * Initialize block
     */
    public function __construct()
    {
        $this->setTemplate('[namespace]_[module]/urls.phtml');
    }

    /**
     * Render HTML
     *
     * @param Varien_Data_Form_Element_Abstract $element
     * @return string
     */
    public function render(Varien_Data_Form_Element_Abstract $element)
    {
        $this->setElement($element);
        return $this->toHtml();
    }

}

And the template that renders the filled in options and adds a javascript template for the other rows.

app/design/adminhtml/default/default/template/[namespace]_[module]/urls.phtml

$_htmlId      = $this->getElement()->getHtmlId();
$_htmlClass   = $this->getElement()->getClass();
$_htmlName    = $this->getElement()->getName();
$_readonly    = $this->getElement()->getReadonly();

$collection = Mage::registry('[namespace]_[module]_data')->getUrls(); // this gets the URLs from the model that is loaded for this item, so the class [Namespace]_[Module]_Model_[Object] needs a method `getUrls`

$_counter = 0;
?>
<tr>
    <td class="label"><?php echo $this->getElement()->getLabel() ?></td>
    <td colspan="10" class="grid hours">
        <table id="attribute-options-table" class="data border [module]-urls" cellspacing="0" cellpadding="0"><tbody>
            <tr class="headings">
                <th><?php echo $this->__('Label') ?></th>
                <th><?php echo $this->__('Url') ?></th>
                <th class="last"><button id="add_new_option_button" title="Add Option" type="button" class="scalable add"><span><span><span><?php echo $this->__('Add Option') ?></span></span></span></button></th>
            </tr>
<?php foreach ($collection as $_item): ?>
<tr class="option-row [module]-urls-row" id="urls-row-<?php echo $_counter?>">
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][label]" value="<?php echo $_item->getLabel() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][url]" value="<?php echo $_item->getUrl() ?>" class="input-text" type="text"></td>
    <td class="a-left" id="delete_button_container_option_<?php echo $_counter ?>'">
        <input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][id]" value="<?php echo $_item->getId() ?>" type="hidden">
        <input id="delete-row-<?php echo $_counter ?>" type="hidden" class="delete-flag" name="<?php echo $_htmlName; ?>[delete][option_<?php echo $_counter ?>]" value=""/>
        <button onclick="$('hour-row-<?php echo $_counter ?>').style.display='none'; $('delete-row-<?php echo $_counter ?>').setValue(1);" title="Delete" type="button" class="scalable delete delete-option"><span><span><span>Delete</span></span></span></button>
    </td>
</tr>
<?php
        $_counter++;
    endforeach;
?>
</tbody></table>

<script type="text/javascript">//<![CDATA[

var _form_html_row = '<tr class="option-row [module]-urls-row" id="urls-row-{{id}}"><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][label]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][url]" value="" class="input-text" type="text"></td><td class="a-left" id="delete_button_container_option_{{id}}"><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][id]" value="" type="hidden"><input id="delete-row-{{id}}" type="hidden" class="delete-flag" name="<?php echo $_htmlName; ?>[delete][option_{{id}}]" value=""/><button onclick="$(\'urls-row-{{id}}\').style.display=\'none\'; $(\'delete-row-{{id}}\').setValue(1);" title="Delete" type="button" class="scalable delete delete-option"><span><span><span>Delete</span></span></span></button></td></tr>';

var _urls_counter = <?php echo $_counter?>;

$('add_new_option_button').observe('click', function(){
    $('attribute-options-table').insert(_form_html_row.replace(/\{\{id\}\}/ig, _urls_counter));
    _urls_counter++;
});

//]]></script>
    </td>
</tr>

Storing the data on save

app/code/[codepool]/[Namespace]/[Module]/controllers/Adminhtml/[Module]Controller.php

class [Namespace]_[Module]_Adminhtml_[Module]Controller 
 extends Mage_Adminhtml_Controller_Action
{

    public function saveAction()
    {
        if ( $this->getRequest()->getPost() ) 
        {
            try {
                $postData = $this->getRequest()->getPost();

                // Save the data
                $model = Mage::getModel('[namespace]_[module]/object');

                $urls = $postData['urls'];
                unset($postData['urls']);
                $model->setData($postData);

                $model->save();


                $parentId = $model->getId();

                // save urls
                if (!empty($urls))
                {
                    foreach ($urls['delete'] as $_key => $_row)
                    {
                        $delete = (int)$_row;
                        $urlsData = $urls['value'][$_key];

                        $_urls = Mage::getModel('[namespace]_[module]/[urls]')->load((int)$urlsData['id']); // this is the model that stores the URLs data in a second table

                        if ($delete && 0 < (int)$_urls->getId()) // exists & required to delete
                        {
                            $_urls->delete();
                            continue;
                        }

                        if (!$delete)
                        {
                            if (0 == (int)$_urls->getId()) // new item
                            {
                                Mage::getModel('[namespace]_[module]/[urls]')->setData(array(
                                    'parent_id' => $parentId,
                                    'label' => $urlsData['label'],
                                    'url'   => $urlsData['url'],
                                    'sortorder' => $urlsData['sortorder'],
                                ))->save();
                            }
                            else
                            {
                                $_urls->addData(array(
                                    'store_id'  => $parentId,
                                    'label' => $urlsData['label'],
                                    'url'   => $urlsData['url'],
                                    'sortorder' => $urlsData['sortorder'],
                                ))->save();
                            }
                        }
                    }
                }

                // And wrap up the transaction
                Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('Item was successfully saved'));
                [...]

                $this->_redirect('*/*/');
                return;
            } catch (Exception $e) {
                [...]
            }
        }
        $this->_redirect('*/*/');
    }

}

Now earlier I've referenced to the method getUrls in the objects model. It would look something like this.

app/code/[codepool]/[Namespace]/[Module]/Model/[Object].php

class [Namespace]_[Module]_Model_[Object] extends Mage_Core_Model_Abstract
{

    public function getOpeningHours()
    {
        return Mage::getModel('[namespace]_[module]/urls')->getCollection()
                    ->addFieldToFilter('parent_id', $this->getId());
    }
}

And that's it!

Related Topic