在ExtJS中使用网格作为字段
在ExtJS中,将模型属性绑定到表单字段非常容易:在ExtJS中使用网格作为字段,extjs,mvvm,data-binding,extjs6,two-way-binding,Extjs,Mvvm,Data Binding,Extjs6,Two Way Binding,在ExtJS中,将模型属性绑定到表单字段非常容易: // ...skipped everything until fields config for brevity }, { xtype: 'textfield', bind: '{modelInstance.someField}' }, { // ... 在这种情况下,由于双向绑定,modeldinstancestring字段someField将与textbox值同步。这很好 我想要实现的是在模型字段不是字符串而是数组的情况下获得相同
// ...skipped everything until fields config for brevity
}, {
xtype: 'textfield',
bind: '{modelInstance.someField}'
}, { // ...
在这种情况下,由于双向绑定,modeldinstance
string字段someField
将与textbox值同步。这很好
我想要实现的是在模型字段不是字符串而是数组的情况下获得相同的行为。这就是模型:
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
fieldhardField
目前我有:
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
- 如何绑定
config来存储数据,以便在我们遍历网格时更新数据gridArray
- 以优雅的MVVM方式执行所有这些操作,而无需编写大量侦听器来强制JS对象之间的同步
请提供已知有效的经过测试的解决方案,我自己已经尝试了许多不同的方法,但迄今为止都没有成功。这里是一个实现此绑定的实用工具。简单的方法是将数组字段与存储的“data”属性绑定。 对于您所做的工作,一个很好的建议是避免在通用组件(gridfield)内定义viewmodel,而只在特定于应用程序的视图上使用viewmodels。 在通用组件上,您应该只使用setter/getter/update逻辑定义配置属性,以便能够将它们与bind一起使用。在这种情况下,不需要自定义属性,因为存储就足够了 编辑 为了避免“轻松绑定”,可以在girdfield组件中实现数组的set/get逻辑。例如,使用setter调用的“UpdateGridaray”方法和存储的“datachanged”事件。 小提琴被更新,示例girdfield使用单元格编辑插件来显示双向绑定 小提琴:
这里是一个工作小提琴,以实现这种绑定。简单的方法是将数组字段与存储的“data”属性绑定。 对于您所做的工作,一个很好的建议是避免在通用组件(gridfield)内定义viewmodel,而只在特定于应用程序的视图上使用viewmodels。 在通用组件上,您应该只使用setter/getter/update逻辑定义配置属性,以便能够将它们与bind一起使用。在这种情况下,不需要自定义属性,因为存储就足够了 编辑 为了避免“轻松绑定”,可以在girdfield组件中实现数组的set/get逻辑。例如,使用setter调用的“UpdateGridaray”方法和存储的“datachanged”事件。 小提琴被更新,示例girdfield使用单元格编辑插件来显示双向绑定 小提琴:
谢谢你,男爵!将存储数据绑定到模型字段的想法很简单,但有缺陷。我们别无选择,只能更新原始模型数组中的记录,而不是网格存储中的记录,因为存储不会将更新推回到模型。这意味着网格必须具备viewmodel的一些知识,这打破了它的一般性。此外,它不知道如何向用户显示添加/更新视图。我稍后会发布我的解决方案,这并不理想,但就目的而言,它是可以的。你是对的。这是一个干净的解决方案。我用一个“gridfield”组件更新了fiddle,该组件只需要“gridArray”作为可绑定配置谢谢Baron!将存储数据绑定到模型字段的想法很简单,但有缺陷。我们别无选择,只能更新原始模型数组中的记录,而不是网格存储中的记录,因为存储不会将更新推回到模型。这意味着网格必须具备viewmodel的一些知识,这打破了它的一般性。此外,它不知道如何向用户显示添加/更新视图。我稍后会发布我的解决方案,这并不理想,但就目的而言,它是可以的。你是对的。这是一个干净的解决方案。我用“gridfield”组件更新了fiddle,该组件只需要“gridArray”作为可绑定配置
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'
});