1

在 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其存储数据扩展并同步到modelInstancefield 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 对象之间同步的侦听器。

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

4

1 回答 1

0

这是实现此绑定的工作小提琴。简单的方法是将数组字段与商店的“数据”属性绑定。对您所做工作的一个很好的建议是避免在通用组件(网格字段)内定义视图模型,但仅在您的应用程序特定视图上使用视图模型。在您的通用组件上,您应该仅使用 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'
    });
于 2017-11-25T18:03:38.967 回答