Form with textfield and grid: send all values to t

2019-08-09 22:42发布

问题:

In create and update forms, it is sometimes necessary to give the user the ability to dynamically add fields to values of the same type (more than one phone, more than one address, etc.).

I'm exploring several possibilities to do this.

One of them is to use a grid as a form field.

However, I have doubts about how best to implement this idea, especially on how to send all the form field values (textfield and grid) to the server (and then how to load them later in the form to edit).

Fiddles with some ideas:

One with cellediting plugin https://fiddle.sencha.com/#view/editor&fiddle/2ftp

Another one with roweditin gplugin a https://fiddle.sencha.com/#view/editor&fiddle/2fto

回答1:

Not sure about the "best to implement", but I have seen so many requirements for multivalue input, that for reusability I have in my toolbox a gridfield similar to the following one:

Ext.define('Ext.ux.GridField', {
    extend: 'Ext.form.FieldContainer',
    alias: 'widget.gridfield',
    initComponent: function () {
        var me = this;
        if(!me.columns) me.columns = {
             dataIndex: 'field1'
        };
        if(!me.mapFn) me.mapFn = function(value) {
            if(Ext.isObject(value)) return value;
            return {
                field1: value
            };
        };
        if(!me.unmapFn) me.unmapFn = function(record) {
            return record.get('field1');
        };
        me.grid = Ext.widget(Ext.apply({
            xtype: 'grid',
            viewConfig: {
                markDirty: false
            },
            store: me.store || Ext.create('Ext.data.Store', {
                fields: me.columns.map(function(column) {
                    return {
                        name: column.dataIndex,
                        type: column.dataType || 'auto',
                        defaultValue: column.defaultValue
                    };
                }),
                listeners: {
                    update: me.updateValue,
                    datachanged: me.updateValue,
                    scope: me
                }
            }),
            columns: [{
                xtype: 'actioncolumn',
                getClass: function () {
                    return 'x-fa fa-times'
                },
                handler: function(grid, rowIndex, colIndex, item, e, record) {
                    grid.getStore().remove(record);
                },
                width: 35
            }].concat(me.columns),
            bbar: [{
                xtype: 'button',
                iconCls: 'x-fa fa-pencil',
                text: 'Add',
                handler: function(btn) {
                    var grid = btn.up('grid'),
                        store = grid.getStore(),
                        record = store.add(Ext.clone(me.emptyRecord) || {})[0];
                    grid.getPlugin('editor').startEditByPosition({
                        row: store.indexOf(record),
                        column: 1
                    });
                }
            }],
            plugins: [
                Ext.create('Ext.grid.plugin.CellEditing', {
                    pluginId: 'editor',
                    clicksToEdit: 1
                })
            ]
        }, me.gridConfig)); // "gridConfig" config can override everything on each instance.
        me.hiddenField = Ext.widget({
            xtype: 'hiddenfield',
            name: me.name,
            value: '',
            allowNull: false,
            rawToValue: function (raw) {
                return raw;
            },
            valueToRaw: function (value) {
                return value;
            },
            getRawValue: function () {
                return Ext.valueFrom(this.rawValue, '')
            },
            isEqual: function (a, b) {
                return Ext.encode(a) == Ext.encode(b)
            },
            listeners: {
                change: function(field, nV, oV) {
                    if(!Ext.isArray(nV)) nV = [nV];
                    var store = me.grid.getStore();
                    store.removeAll();
                    store.add(nV.map(me.mapFn));
                }
            }
        });
        Ext.apply(me, {
            layout: 'fit',
            items: [{
                xtype:'container',
                border: 1,
                style: {
                    borderColor: '#d0d0d0',
                    borderStyle: 'solid'
                },
                items: [me.grid]
            }, me.hiddenField]
        });
        me.callParent(arguments);
    },
    updateValue: function() {
        var me = this,
            grid = me.grid,
            hiddenField = me.hiddenField,
            nV = grid.getStore().getRange().map(me.unmapFn, me),
            oV = me.hiddenField.getValue();
        if(!oV || Ext.isArray(oV) && Ext.encode(nV) != Ext.encode(oV)) {
            hiddenField.suspendCheckChange++;
            hiddenField.setValue(nV);
            hiddenField.suspendCheckChange--;
            me.fireEvent('change', me, nV, oV);
        }
    }
});

which can then be used like this:

},{
    xtype: 'gridfield',
    fieldLabel: 'Contacts',
    name: 'contacts',
    columns: [{
        text: 'Type',
        dataIndex: 'type',
        editor:{
            xtype: 'combobox',
            name: 'type',
            valueField: 'name',
            displayField: 'name',
            store: combostore,
            queryMode: 'local'
        },
        flex: 0.7
    },{
        text: 'Description',
        dataIndex: 'description',
        editor:{
            xtype: 'textfield',
            name: 'description'
        },
        flex: 1
    }],
    mapFn: function(value) {
        return value;
    },
    unmapFn: function(record) {
        return record.getData();
    }
}, {

I have made a fiddle for you based on your fiddle, including working load and save operations on the form, but in ExtJS 6.x. And I have checked that it works with ExtJS 5 as well, although you have to add working icons.



标签: Extjs extjs5