Search code examples
extjsshopware

Extend Shopware Emotion Banner Slider


We try to create a custom banner slider for emotion in Shopware. This custom banner is derived from a standard emotion banner slider and adds some additional columns to it. Everything works perfectly and there is no errors, however, the model is completely ignored, i.e. the data from grid is not saved, so the add/save event is not fired. (This question appears here in German) This is our ExtJS component:

/**
 * ExtJS Component for Media Widget Plugin
 */
Ext.define('Shopware.apps.Emotion.view.components.AdvancedBannerSlider', {

    /**
     * Extend from the base emotion component.
     */
    extend: 'Shopware.apps.Emotion.view.components.BannerSlider',

    /**
     * Set the defined xtype of the component as the new widget alias.
     */
    alias: 'widget.emotion-components-advancedbannerslider',

    /**
     * Snippets for the component.
     * @object
     */
    snippets: {
        'select_banner': '{s name=select_banner}Select banner(s){/s}',
        'banner_administration': '{s name=banner_administration}Banner administration{/s}',
        'path': '{s name=path}Image path{/s}',
        'actions': '{s name=actions}Action(s){/s}',
        'link': '{s name=link}Link{/s}',
        'altText': '{s name=altText}Alternative text{/s}',
        'title': '{s name=title}Title{/s}',
        'size': '{s name=size}Größe{/s}',
        'cols': '{s name=cols}Spalten{/s}',
        'filter': '{s name=filter}Filter{/s}',

        banner_slider_title: '{s name=banner_slider_title}Title{/s}',
        banner_slider_arrows: '{s name=banner_slider_arrows}Display arrows{/s}',
        banner_slider_numbers: {
            fieldLabel: '{s name=banner_slider_numbers/label}Display numbers{/s}',
            supportText: '{s name=banner_slider_numbers/support}Please note that this setting only affects the "emotion" template.{/s}'
        },
        banner_slider_scrollspeed: '{s name=banner_slider_scrollspeed}Scroll speed{/s}',
        banner_slider_rotation: '{s name=banner_slider_rotation}Rotate automatically{/s}',
        banner_slider_rotatespeed: '{s name=banner_slider_rotatespeed}Rotation speed{/s}'
    },


    /**
     * Creates the fieldset which holds the banner administration. The method
     * also creates the banner store and registers the drag and drop plugin
     * for the grid.
     *
     * @public
     * @return [object] Ext.form.FieldSet
     */
    createBannerFieldset: function() {
        var me = this;

        me.mediaSelection = Ext.create('Shopware.form.field.MediaSelection', {
            fieldLabel: me.snippets.select_banner,
            labelWidth: 155,
            albumId: -3,
            listeners: {
                scope: me,
                selectMedia: me.onAddBannerToGrid
            }
        });

        me.bannerStore = Ext.create('Ext.data.Store', {
            fields: [ 'position', 'path', 'link', 'altText', 'title', 'mediaId', 'size', 'cols', 'filter' ]
        });

        me.ddGridPlugin = Ext.create('Ext.grid.plugin.DragDrop');

        me.cellEditing = Ext.create('Ext.grid.plugin.RowEditing', {
            clicksToEdit: 2
        });

        me.bannerGrid = Ext.create('Ext.grid.Panel', {
            columns: me.createColumns(),
            autoScroll: true,
            store: me.bannerStore,
            height: 200,
            plugins: [ me.cellEditing ],
            viewConfig: {
                plugins: [ me.ddGridPlugin ],
                listeners: {
                    scope: me,
                    drop: me.onRepositionBanner
                }
            },
            listeners: {
                scope: me,
                edit: function() {
                    me.refreshHiddenValue();
                }
            }
        });

        return me.bannerFieldset = Ext.create('Ext.form.FieldSet', {
            title: me.snippets.banner_administration,
            layout: 'anchor',
            defaults: { anchor: '100%' },
            items: [ me.mediaSelection, me.bannerGrid ]
        });
    },


    /**
     * Helper method which creates the column model
     * for the banner administration grid panel.
     *
     * @public
     * @return [array] computed columns
     */
    createColumns: function() {
        var me = this, snippets = me.snippets;

        return [{
            header: '⚌',
            width: 24,
            hideable: false,
            renderer : me.renderSorthandleColumn
        }, {
            dataIndex: 'path',
            header: snippets.path,
            flex: 1
        }, {
            dataIndex: 'link',
            header: snippets.link,
            flex: 1,
            editor: {
                xtype: 'textfield',
                allowBlank: true
            }
        }, {
            dataIndex: 'altText',
            header: snippets.altText,
            flex: 1,
            editor: {
                xtype: 'textfield',
                allowBlank: true
            }
        }, {
            dataIndex: 'title',
            header: snippets.title,
            flex: 1,
            editor: {
                xtype: 'textfield',
                allowBlank: true
            }
        }, {
            dataIndex: 'size',
            header: snippets.size,
            flex: 1,
            editor: {
                xtype: 'textfield',
                allowBlank: true
            }
        }, {
            dataIndex: 'cols',
            header: snippets.cols,
            flex: 1,
            editor: {
                xtype: 'numberfield',
                allowBlank: true,
                minValue: 1,
                decimalPrecision: 0
            }
        }, {
            dataIndex: 'filter',
            header: snippets.filter,
            flex: 1,
            editor: {
                xtype: 'textfield',
                allowBlank: true
            }
        }, {
            xtype: 'actioncolumn',
            header: snippets.actions,
            width: 60,
            items: [{
                iconCls: 'sprite-minus-circle',
                action: 'delete-banner',
                scope: me,
                handler: me.onDeleteBanner
            }]
        }];
    },


    /**
     * Event listener method which will be triggered when one (or more)
     * banner are added to the banner slider.
     *
     * Creates new models based on the selected banners and
     * assigns them to the banner store.
     *
     * @public
     * @event selectMedia
     * @param [object] field - Shopware.MediaManager.MediaSelection
     * @param [array] records - array of the selected media
     */
    onAddBannerToGrid: function(field, records) {
        var me = this, store = me.bannerStore;

        Ext.each(records, function(record) {
            var count = store.getCount();
            var model = Ext.create('Shopware.apps.Emotion.model.AdvancedBannerSliderModel', {
                position: count,
                path: record.get('path'),
                mediaId: record.get('id'),
                link: record.get('link'),
                altText: record.get('altText'),
                title: record.get('title'),
                size: record.get('size'),
                cols: record.get('cols'),
                filter: record.get('filter')
            });
            store.add(model);
        });

        // We need a defer due to early firing of the event
        Ext.defer(function() {
            me.mediaSelection.inputEl.dom.value = '';
            me.refreshHiddenValue();
        }, 10);

    },

    getGridData: function() {
        var me = this,
            elementStore = me.getSettings('record').get('data'), advancedBannerSlider;
        Ext.each(elementStore, function(element) {
            if(element.key === 'advanced_banner_slider') {
                advancedBannerSlider = element;
                return false;
            }
        });

        if(advancedBannerSlider && advancedBannerSlider.value) {
            Ext.each(bannerSlider.value, function(item) {
                me.bannerStore.add(Ext.create('Shopware.apps.Emotion.model.AdvancedBannerSliderModel', item));
            });
        }
    },

});


//{block name="backend/emotion/controller/massive_widgets"}
//{namespace name=backend/emotion/controller}
Ext.define('Shopware.apps.Emotion.controller.AdvancedBannerSlider', {

    /**
     * Defines an override applied to a class.
     * @string
     */
    extend: 'Shopware.apps.Emotion.controller.Detail'
});
//{/block}


/**
 * Shopware Model - Emotion backend module.
 */
//{block name="backend/emotion/model/banner_slider"}
Ext.define('Shopware.apps.Emotion.model.AdvancedBannerSliderModel', {
    /**
     * Extends the standard Ext Model
     * @string
     */
    extend: 'Shopware.apps.Emotion.model.BannerSlider',

    /**
     * The fields used for this model
     * @array
     */
    fields: [
        //{block name="backend/emotion/model/field/banner_slider"}{/block}
        'position', 'path', 'mediaId', 'link', 'altText', 'title', 'size', 'cols', 'filter'
    ]
});
//{/block}

This is the structure of our plugin: plugin strucutre

So if necessary I can provide a GitLab link. Thank you in advance!


Solution

  • Thank everyone for help! The problem was that the hidden field for banner slider in json format was missed, it brings data to the model. So, the solution: 1. add this field to the new component

     $element = $installer->createOrUpdate(
            $this->getName(),
            'advancedbannerslider',
            [
                'name' => 'advancedbannerslider',
                'xtype' => 'emotion-advanced-banner-slider',
                'template' => 'emotion_advanced_banner_slider',
                'cls' => 'emotion_advanced_banner_slider',
                'description' => 'advancedbannerslider'
            ]
        );
    
    $element->createHiddenField([
        'name' => 'banner_slider',
        'valueType' => 'json',
    ]);
    
    1. some adjustments to the code from above: alias: 'widget.emotion-advanced-banner-slider', and
          getGridData: function() {
                var me = this,
                    elementStore = me.getSettings('record').get('data'), bannerSlider;
    
                Ext.each(elementStore, function(element) {
                    if(element.key === 'banner_slider') {
                        bannerSlider = element;
                        return false;
                    }
                });
    
                if(bannerSlider && bannerSlider.value) {
                    Ext.each(bannerSlider.value, function(item) {
                        me.bannerStore.add(Ext.create('Shopware.apps.Emotion.model.AdvancedBannerSliderModel', item));
                    });
                }
            },