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
2 Answers
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
- entity_id
- parent_id
- label
- url
- 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!
-
Do we need to do anything on the layout file in adminhtml? Commented Mar 13, 2015 at 12:13
-
-
-
did you check if the block classes are loaded, if it can find the phtml file etc? Please try to pinpoint the issue Commented Mar 13, 2015 at 12:48
-
It is not able to find the phtml file I think . I am just a beginner in Magento. Maybe that's why I don't know how to bring it. Commented Mar 13, 2015 at 12:55
It's easier than that. I have described it in full at http://www.integer-net.com/2015/03/17/how-to-create-tables-in-magento-system-configuration/. In short:
system.xml:
<shipping_costs translate="label">
<label>Shipping Cost based on Price</label>
<frontend_model>namespace_module/config_shippingCosts</frontend_model>
<backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</shipping_costs>
As you can see, the code only adds a frontend model and a backend model to the usual definition of new fields.
The backend model is used to modify saving the field value to and loading from the database. Always use adminhtml/system_config_backend_serialized_array here. The frontend model is in fact a block, despite its name. It is used to render the table with the defined columns. The frontend models always inherits from the class Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract.
In my example the frontend model class looks like this:
<?php
class Namespace_Module_Block_Config_ShippingCosts
extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract
{
public function _prepareToRender()
{
$this->addColumn('from_price', array(
'label' => Mage::helper('namespace_module')->__('From Price'),
'style' => 'width:100px',
));
$this->addColumn('cost', array(
'label' => Mage::helper('namespace_module')->__('Shipping Cost'),
'style' => 'width:100px',
));
$this->_addAfter = false;
$this->_addButtonLabel = Mage::helper('namespace_module')->__('Add');
}
}
Nothing more is needed to display a table with two columns of 100px each.
If you want to use the values from the configuration somewhere, you just have to unserialize the field data:
$shippingCosts = Mage::getStoreConfig('namespace_module/general/shipping_costs');
if ($shippingCosts) {
$shippingCosts = unserialize($shippingCosts);
if (is_array($shippingCosts)) {
foreach($shippingCosts as $shippingCostsRow) {
$fromPrice = $shippingCostsRow['from_price'];
$cost = $shippingCostsRow['cost'];
...
}
} else {
// handle unserializing errors here
}
}