【问题标题】:Using grid as field in ExtJS在 ExtJS 中使用网格作为字段
【发布时间】:2017-11-23 07:10:50
【问题描述】:

在 ExtJS 中将模型属性绑定到表单字段非常简单:

// ...skipped everything until fields config for brevity
}, {
  xtype: 'textfield',
  bind: '{modelInstance.someField}'
}, { // ...

在这种情况下,modelInstance 字符串字段someField 将同步到文本框的值,这要归功于两种方式的绑定。这很棒。

我想要实现的是在模型字段不是字符串而是数组的情况下获得相同的行为。这是模型:

Ext.define('namespace.model.CustomModel', {
  fields: ['easyField', {
    name: 'hardField',
    type: 'auto'  // This will be an array during runtime
  }],
  idProperty: 'easyField'
});

我想做这样的事情:

// ...skipped everything until fields config for brevity, 
// assume that viewmodel and everything else are set up as expected
}, {
  xtype: 'textfield',
  bind: '{modelInstance.easyField}'
}, {
  xtype: 'gridfield',
  bind: {
    gridArray: '{modelInstance.hardField}'
  }
}, { // ...

可以理解,我希望gridfield组件扩展Ext.grid.Panel并将其存储数据同步到modelInstance字段hardField

目前我有这个:

Ext.define('namespace.form.field.GridField', {
  extends: 'Ext.grid.Panel',
  xtype: 'gridfield',
  // skip requires for brevity
  viewModel: {
    stores: {
      gridFieldItems: {
        type: 'gridfielditems'  // Simple in memory store
      }
    },
    data: {
    }
  },
  store: '{gridFieldItems}',
  // This config enables binding to take place as it creates getters and setters,
  // gridArray is set initially to '{modelInstance.hardField}' as expected
  config: {
    gridArray: null
  },
  // This is necessary for this grid-field component to update 
  // '{modelInstance.hardField}' back in the owners viewModel. 
  publishes: {
    gridArray: true
  },

  // ???
  // bind: {
  //   gridArray: bind gridArray to store data somehow?
  // }

});

问题来了:

  • 如何注入现有的modelInstance.hardField 数组作为gridFieldItems 存储初始数据,
  • 如何绑定 gridArray 配置来存储数据,以便在我们对网格进行粗加工时更新,
  • 以优雅的 MVVM 方式完成所有这些操作,而无需编写一堆试图强制 JS 对象之间同步的侦听器。

请提供已知可行的经过测试的解决方案,我自己已经尝试了很多不同的方法,但到目前为止都没有成功。

【问题讨论】:

    标签: extjs mvvm data-binding extjs6 two-way-binding


    【解决方案1】:

    这是实现此绑定的工作小提琴。简单的方法是将数组字段与商店的“数据”属性绑定。 对您所做工作的一个很好的建议是避免在通用组件(网格域)内定义视图模型,而仅在您的应用程序特定视图上使用视图模型。 在您的通用组件上,您应该仅使用 setter/getter/update 逻辑定义配置属性,以便能够将它们与绑定一起使用。在这种情况下,不需要自定义属性,因为商店就足够了。

    编辑 为避免“简单绑定”,您可以在 girdfield 组件中实现数组的设置/获取逻辑。例如,使用 setter 调用的“updateGridArray”方法和 store 的“datachanged”事件。 小提琴已更新,示例 girdfield 使用单元格编辑插件来显示 2-way 绑定。

    小提琴:https://fiddle.sencha.com/#view/editor&fiddle/2a3b

        Ext.define('Fiddle.model.CustomModel', {
            extend: 'Ext.data.Model',
            fields: ['easyField', {
                name: 'hardField',
                type: 'auto' // This will be an array during runtime
            }],
            idProperty: 'easyField'
        });
        Ext.define('Fiddle.fiddleview.CustomViewModel', {
            extend: 'Ext.app.ViewModel',
            alias: 'viewmodel.fiddleview',
    
            data: {
                currentModel: null
            }
        });
        Ext.define('Fiddle.form.field.GridField', {
            extend: 'Ext.grid.Panel',
            xtype: 'gridfield',
    
            config: {
                gridArray: null
            },
    
            publishes: [
                'selection',
                'gridArray'
            ],
    
            selModel: 'cellmodel',
            plugins: {
                cellediting: {
                    clicksToEdit: 1
                }
            },
    
            columns: [{
                text: 'Column 1',
                flex: 1,
                editor: true,
                dataIndex: 'field1'
            }],
    
            initComponent: function () {
                this.store = {
                    fields: ['field1'],
                    data: [],
                    listeners: {
                        scope: this,
                        datachanged: function (store) {
                            this.setGridArray(store.getRange().map(function (record) {
                                return record.getData();
                            }));
                        }
                    }
                };
    
                this.callParent();
            },
    
            updateGridArray: function (gridArray) {
                this.getStore().suspendEvent('datachanged');
                if (Ext.isEmpty(gridArray)) {
                    this.getStore().removeAll();
                } else {
                    this.getStore().loadData(gridArray);
                }
                this.getStore().resumeEvent('datachanged');
            }
    
        });
    
        var myView = Ext.create('Ext.container.Container', {
            renderTo: Ext.getBody(),
    
            viewModel: 'fiddleview',
    
            items: [{
                xtype: 'gridfield',
                bind: {
                    gridArray: '{currentModel.hardField}'
                }
            }]
        });
    
        // Bind on model's array to check two-way update
        // It will execute also on cell edit
        myView.getViewModel().bind('{currentModel.hardField}', function (value) {
            window.alert('model\'s array changed');
        });
    
        // The binding is now active, when the "currentModel" in the viewmodel changes, grid rows are refresched
        myView.getViewModel().set('currentModel', new Fiddle.model.CustomModel({
            hardField: [{
                field1: 'value1'
            }]
        }));
        // Also when data changes in the "currentModel"
        myView.getViewModel().get('currentModel').get('hardField').push({
            field1: 'value2'
        });
    

    【讨论】:

    • 谢谢男爵!将存储数据绑定到模型字段的想法很简洁,但存在缺陷。我们别无选择,只能更新原始模型数组而不是网格存储中的记录,因为存储不会将更新推送回模型。这意味着网格必须具备一些视图模型知识,这打破了它的通用性。此外,它不知道如何向用户呈现添加/更新视图。我稍后会发布我的解决方案,它并不理想,但可以满足目的。
    • 你是对的。那是一个干净的仅绑定解决方案。我用只需要“gridArray”作为可绑定配置的“gridfield”组件更新了小提琴
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    相关资源
    最近更新 更多