Cara terbaik untuk menambahkan kisi dinamis sebagai input dalam ekstensi Adminhtml kustom

15

Saya telah membangun ekstensi pencari lokasi toko kustom dengan kisi itu sendiri dan mengedit halaman di Adminhtml dan semuanya berfungsi dengan baik. Untuk jam buka toko saya ingin menerapkan kotak dinamis seperti untuk opsi atribut.

masukkan deskripsi gambar di sini

Sekarang saya telah menemukan solusi tetapi saya berharap ada cara yang lebih baik, atau setidaknya lebih bersih. Apa yang saya miliki sejauh ini adalah menambahkan renderer ke bidang dalam formulirfieldset

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Tab_General extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        $form = new Varien_Data_Form();
        $this->setForm($form);
        $fieldset = $form->addFieldset('rkstorelocator_form', array('legend'=>Mage::helper('rkstorelocator')->__('Store information')));

        [...]

        $officehours_field = $fieldset->addField('office_hours', 'editor', array(
            'name'      => 'office_hours',
            'label'     => Mage::helper('rkstorelocator')->__('Office hours'),
            'required'  => false,
        ));

        $officehours_block = $this->getLayout()
                                ->createBlock('rkstorelocator/adminhtml_rkstorelocator_edit_renderer_officehours')
                                ->setData(array(
                                    'name'      => 'office_hours',
                                    'label'     => Mage::helper('rkstorelocator')->__('Office hours'),
                                    'required'  => false,
                                ));

        $officehours_field->setRenderer($officehours_block);

        [...]
    }
}

Dan kelas blok untuk di-render

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Renderer_Officehours
 extends Mage_Adminhtml_Block_Abstract 
 implements Varien_Data_Form_Element_Renderer_Interface 
{

    public function render(Varien_Data_Form_Element_Abstract $element) 
    {
        $required_indicator = $this->getData('required') ? '<span class="required">*</span>' : '' ;

        $html = '
<table id="attribute-options-table" class="dynamic-grid rkstorelocator-officehours" cellspacing="0" cellpadding="0"><tbody>
    <tr>
        <th>Day indicator</th>
        <th>Opening hour</th>
        <th>Closing hour</th>
        <th>
            <button id="add_new_option_button" title="Add Option" type="button" class="scalable add"><span><span><span>Add Option</span></span></span></button>
        </th>
    </tr>
</tbody></table>

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

var _form_html_row = \'<tr class="option-row rkstorelocator-officehours-dayrow" id="hour-row-{{id}}"><td><input name="'.$this->getData('name').'[value][option_{{id}}][0]" value="" class="input-text required-option" type="text"></td><td><input name="'.$this->getData('name').'[value][option_{{id}}][2]" value="" class="input-text required-option" type="text"></td><td><input name="'.$this->getData('name').'[value][option_{{id}}][2]" value="" class="input-text required-option" type="text"></td><td class="a-left" id="delete_button_container_option_{{id}}"><input type="hidden" class="delete-flag" name="'.$this->getData('name').'[delete][option_{{id}}]" value=""/><button onclick="$(\\\'hour-row-{{id}}\\\').remove();" title="Delete" type="button" class="scalable delete delete-option"><span><span><span>Delete</span></span></span></button></td></tr>\';

var _rkstorelocator_counter = 0;

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

//]]></script>
        ';
        return $html;
    }

}

Yang memberi saya hasil berikut masukkan deskripsi gambar di sini

Sekarang ini pada dasarnya bekerja tetapi mendapatkan nilai saat ini di sana akan sangat berantakan dan semuanya saya tidak terlalu bangga dengan kode yang saya tulis (seperti yang Anda bayangkan).

Saya sudah mencari di Google untuk beberapa solusi tetapi semua umumnya mengambil pendekatan ini. Adakah yang tahu cara yang lebih bersih untuk melakukan ini?

Sander Mangel
sumber
Dapatkah saya memiliki kode lengkap dari setiap file yang digunakan untuk modul khusus ini.
UdayPavan Reddy

Jawaban:

10

Butuh waktu lama bagi saya untuk menyadari bahwa harga Tier terlihat seperti yang saya inginkan. Jadi setelah melihat bagaimana Magento melakukannya dengan harga Tier, saya akhirnya melakukan hal berikut. Maaf sebelumnya untuk blok kode yang sangat besar tapi saya pikir mungkin menarik untuk referensi di masa mendatang.

Di kelas formulir saya Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Tab_General

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Tab_General extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        $form = new Varien_Data_Form();
        $this->setForm($form);
        $fieldset = $form->addFieldset('rkstorelocator_form', array('legend'=>Mage::helper('rkstorelocator')->__('Store information')));

        [...]

        $officehours_field = $fieldset->addField('office_hours', 'text', array(
            'name'      => 'office_hours',
            'label'     => Mage::helper('rkstorelocator')->__('Office hours'),
            'required'  => false,
        ));

        $office_hours = $form->getElement('office_hours');

        $office_hours->setRenderer(
            $this->getLayout()->createBlock('rkstorelocator/adminhtml_rkstorelocator_edit_renderer_officehours')
        );


        [...]
    }
}

Sekarang untuk kelas blok jam Office Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Renderer_Officehours.

class Redkiwi_Rkstorelocator_Block_Adminhtml_Rkstorelocator_Edit_Renderer_Officehours
 extends Mage_Adminhtml_Block_Widget
 implements Varien_Data_Form_Element_Renderer_Interface
{

    /**
     * Initialize block
     */
    public function __construct()
    {
        $this->setTemplate('rkstorelocator/officehours.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();
    }

}

Dan file .phtml template adminhtml/default/default/template/rkstorelocator/officehours.phtml

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

$collection = Mage::registry('rkstorelocator_data')
                ->getOpeningHours()
                ->setOrder('sortorder', 'ASC');

$_counter = 0;
?>
<tr>
    <td class="label"><?php echo $this->getElement()->getLabel() ?></td>
    <td colspan="10" class="grid hours">
        <table id="attribute-options-table" class="dynamic-grid rkstorelocator-officehours" cellspacing="0" cellpadding="0"><tbody>
            <tr>
                <th><?php echo $this->__('Day label') ?></th><th><?php echo $this->__('Opening hour') ?></th><th><?php echo $this->__('Closing hour') ?></th><th><?php echo $this->__('Sortorder') ?></th>
                <th><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 rkstorelocator-officehours-dayrow" id="hour-row-<?php echo $_counter?>">
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][dayindicator]" value="<?php echo $_item->getDayindicator() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][openinghour]" value="<?php echo $_item->getOpeninghour() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][closinghour]" value="<?php echo $_item->getClosinghour() ?>" class="input-text" type="text"></td>
    <td><input name="<?php echo $_htmlName; ?>[value][option_<?php echo $_counter ?>][sortorder]" value="<?php echo $_item->getSortorder() ?>" 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 rkstorelocator-officehours-dayrow" id="hour-row-{{id}}"><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][dayindicator]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][openinghour]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][closinghour]" value="" class="input-text" type="text"></td><td><input name="<?php echo $_htmlName; ?>[value][option_{{id}}][sortorder]" 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="$(\'hour-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 _rkstorelocator_counter = <?php echo $_counter?>;

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

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

Dan hasilnya: masukkan deskripsi gambar di sini

Para Googler masa depan yang terhormat, Pada saat Anda membaca Magento 2.x ini akan dirilis. Mari berharap Magento membuat hal semacam ini sedikit lebih mudah. :)

Sander Mangel
sumber
Bagaimana Anda menyimpannya di DB? Sebagai string serial?
Sergei Guk
Tidak, saya punya meja kedua untuk jam kantor. Tampak lebih baik daripada cerita bersambung
Sander Mangel
setuju, hanya perlu menambahkan yang sama untuk produk dan akan ada sekitar 10 pasangan nama-nilai. Jadi berpikir apakah menyimpannya sebagai string serial atau membuat tabel dengan 1 pasangan per baris per produk. Terima kasih atas kirimannya!
Sergei Guk
Alasan saya tidak ingin menggunakan data serial adalah karena Anda tidak dapat melakukan apa pun dengannya saat berada di database. Dalam versi selanjutnya dari ekstensi ini saya ingin mencari lokasi berdasarkan waktu buka. Itu tidak mungkin dengan data serial. Sekarang saya punya objek koleksi yang bisa saya gunakan. Data tambahan yang disebabkan oleh tabel kedua adalah trade off kecil
Sander Mangel
Selamatkan hari saya! Terima kasih untuk itu. Saya kira, $ this-> setTemplate ('rkstorelocator / officehours.phtml'); adalah triknya. Berharap ada pendekatan yang lebih baik untuk javascript bit.
xelber
3

Saya memberikan beberapa kode saya berdasarkan template Magento. Mungkin itu akan bermanfaat.
Beberapa antarmuka tab:

<?php
class Ssd_Shower_Block_Adminhtml_Shower_Edit_Tab_Options
    extends Mage_Adminhtml_Block_Template
    implements Mage_Adminhtml_Block_Widget_Tab_Interface
{

    /** set own teplate */
    public function __construct()
    {
        $this->setTemplate('pregnancy/list/options.phtml');
    }

    /** here some implementation of tab interfeys */


    /** options for every row, they will be rendered as dynamic row with inputs */
    public function getOptionValues()
    {
        $period=$this->getData('period');
        $optionsArr = Mage::helper('shower')->getTipList($period);

        $values = array();
        foreach ($optionsArr as $option) {

            $value = array();
            $value['id'] = $option->getId();
            $value['period_id'] = $period->getId();
            $value['tip_content'] = $option->getTip_content();
            $value['sort_order'] = $option->getSort_order();
            $value['update'] = 1;

            $values[] = new Varien_Object($value);
        }

        return $values;
    }

}

?>

Dan template kehamilan / daftar / options.phtml:

<div class="entity-edit" id="manage-options-panel">
    <div class="entry-edit-head">
        <h4 class="icon-head head-edit-form fieldset-legend">Some title</h4>
    </div>
    <div class="box">
        <div class="hor-scroll">
            <table class="dynamic-grid" cellspacing="0" cellpadding="0" width="100%">
                <tr id="grid_head">
                    <th style="width:90%!important"><?php echo Mage::helper('pregnancy')->__('Checklist Items') ?></th>
                    <th class="w-150"><?php echo Mage::helper('pregnancy')->__('Position') ?></th>
                    <th class="w-150">
                        <button id="add_new_option_button" class="scalable add" style="" onclick="" type="button">
                            <span><?php echo Mage::helper('pregnancy')->__('Add Checklist Item') ?></span>
                        </button>
                    </th>
                </tr>
                <tr id="attribute-options-table">
                </tr>
                <tr class="no-display template" id="row-template">
                    <td><input name="tip[{{id}}][tip_content]"
                               value="{{tip_content}}"
                               class="input-text required-option full"
                               type="text" disabled="disabled"/></td>

                    <td class="a-center"><input class="input-text" type="text" name="tip[{{id}}][sort_order]"
                                                value="{{sort_order}}"/></td>
                    <td class="a-left">
                        <input type="hidden" class="delete-flag" name="tip[{{id}}][delete]" value=""/>
                        <input type="hidden" class="update-flag" name="tip[{{id}}][update]" value="{{update}}"/>
                        <button class="scalable delete delete-option" type="button"><span>Delete</span></button>
                    </td>
                </tr>
            </table>
        </div>
        <input type="hidden" id="option-count-check" value=""/>
    </div>
</div>

<script type="text/javascript">
    //<![CDATA[
    var optionDefaultInputType = 'text';
    //template for dynamic row
    var templateText =
            '<tr class="option-row">' +
                    '<td><input name="tip[{{id}}][tip_content]" value="{{tip_content}}" class="input-text required-option full" type="text"/><\/td>' +
                    '<td><input class="input-text" type="text" name="tip[{{id}}][sort_order]" value="{{sort_order}}"/><\/td>' +
                    '<td class="a-left">' +
                    '<input type="hidden" class="delete-flag" name="tip[{{id}}][delete]" value="" />' +
                    '<input type="hidden" class="update-flag" name="tip[{{id}}][update]" value="{{update}}"/>' +
                    '<button class="scalable delete delete-option" type="button"><span><?=$this->__("Delete")?></span></button>' +
                    '<\/td>' +
                    '<\/tr>';

    var attributeOption = {
        table : $('attribute-options-table'),
        templateSyntax : /(^|.|\r|\n)({{(\w+)}})/,
        templateText : templateText,
        itemCount : 0,
        totalItems : 0,
        //add dynamic row function
        add : function(data) {
            this.template = new Template(this.templateText, this.templateSyntax);
            if (!data.id) {
                data = {};
                data.id = 'option_' + this.itemCount;
            }
            if (!data.intype)
                data.intype = optionDefaultInputType;

            Element.insert(this.table, {before: this.template.evaluate(data)});
            this.bindRemoveButtons();
            this.itemCount++;
            this.totalItems++;
            this.updateItemsCountField();
        },
        //remove dynamic row function
        remove : function(event) {
            if (confirm('<?php echo $this->__("Do you really delete this tip?");?>')) {
                var element = $(Event.findElement(event, 'tr'));
                element.ancestors().each(function(parentItem) {
                    if (parentItem.hasClassName('option-row')) {
                        element = parentItem;
                        throw $break;
                    } else if (parentItem.hasClassName('box')) {
                        throw $break;
                    }
                });

                if (element) {
                    var elementFlags = element.getElementsByClassName('delete-flag');
                    if (elementFlags[0]) {
                        elementFlags[0].value = 1;
                    }

                    element.addClassName('no-display');
                    element.addClassName('template');
                    element.hide();
                    this.totalItems--;
                    this.updateItemsCountField();
                }
            }
        },
        updateItemsCountField: function() {
            if (this.totalItems > 0) {
                $('option-count-check').value = '1';
            } else {
                $('option-count-check').value = '';
            }
        },
        bindRemoveButtons : function() {
            var buttons = $$('.delete-option');
            for (var i = 0; i < buttons.length; i++) {
                if (!$(buttons[i]).binded) {
                    $(buttons[i]).binded = true;
                    Event.observe(buttons[i], 'click', this.remove.bind(this));
                }
            }
        }

    }
    if ($('row-template')) {
        $('row-template').remove();
    }
    attributeOption.bindRemoveButtons();

    if ($('add_new_option_button')) {
        Event.observe('add_new_option_button', 'click', attributeOption.add.bind(attributeOption));
    }
    Validation.addAllThese([
        ['required-option', '<?php echo Mage::helper('pregnancy')->__('Failed') ?>', function(v) {
            return !Validation.get('IsEmpty').test(v);
        }]
    ]);
    Validation.addAllThese([
        ['required-options-count', '<?php echo Mage::helper('pregnancy')->__('Options is required') ?>', function(v) {
            return !Validation.get('IsEmpty').test(v);
        }]
    ]);
<?php
    /** pulling data from Ssd_Shower_Block_Adminhtml_Shower_Edit_Tab_Options **/
    if ($options = $this->getOptionValues()) {
        foreach ($options as $_value): ?>
        attributeOption.add(<?php echo $_value->toJson() ?>);
    <?php endforeach; } ?>
    //]]>
</script>
mageUz
sumber
Terima kasih atas tanggapannya, agak seperti bagaimana saya menyelesaikannya. Ada beberapa bagian dari phtml Anda yang ingin saya gunakan :-)
Sander Mangel