我有一个用于显示发票信息的网格。使用Invoice商店填充网格,Invoice商店使用Invoice模型,Invoice模型与InvoiceStatus模型具有“has one”关联,主键为“id”,foren键为“invoice_status_id”。
我不确定如何使发票网格的“状态”列的显示值使用invoice_status_id插入的关联模型“名称”。我知道我需要创建一个渲染器来执行此操作,但我仍然得到一个空值。 Invoice和InvoiceStatus存储都填充了正确的值。
状态栏渲染
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
return record.getStatus().get('name');
},
发票商店
Ext.define('MyApp.store.Invoice', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.InvoiceModel'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: true,
autoSync: true,
model: 'MyApp.model.InvoiceModel',
remoteSort: true,
storeId: 'StoreInvoce',
proxy: {
type: 'rest',
url: '/api/invoice',
reader: {
type: 'json',
root: 'data'
}
}
}, cfg)]);
}
});
InvoiceStatus商店
Ext.define('MyApp.store.InvoiceStatus', {
extend: 'Ext.data.Store',
alias: 'store.InvoiceStatus',
requires: [
'MyApp.model.InvoiceStatus'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: true,
autoSync: true,
model: 'MyApp.model.InvoiceStatus',
remoteSort: true,
storeId: 'MyJsonStore1',
proxy: {
type: 'rest',
url: '/api/invoice_status',
reader: {
type: 'json',
root: 'data'
}
}
}, cfg)]);
}
});
发票型号
Ext.define('MyApp.model.InvoiceModel', {
extend: 'Ext.data.Model',
uses: [
'MyApp.model.InvoiceStatus'
],
fields: [
{
mapping: 'id',
name: 'id',
type: 'int'
},
{
mapping: 'client_id',
name: 'client_id',
type: 'int'
},
{
mapping: 'client_name',
name: 'client_name',
type: 'string'
},
{
dateFormat: 'Y-m-d',
dateReadFormat: '',
mapping: 'issue_date',
name: 'issue_date',
sortType: 'asDate',
type: 'date'
},
{
dateFormat: 'Y-m-d',
mapping: 'due_date',
name: 'due_date',
sortType: 'asDate',
type: 'date'
},
{
mapping: 'payment_date',
name: 'payment_date',
sortType: 'asDate',
type: 'date',
useNull: true
},
{
name: 'amount'
},
{
mapping: 'invoice_status_id',
name: 'invoice_status_id',
sortType: 'asInt',
type: 'int'
}
],
hasOne: {
model: 'MyApp.model.InvoiceStatus',
foreignKey: 'invoice_status_id',
getterName: 'getStatus'
}
});
InvoiceStatus模型
Ext.define('MyApp.model.InvoiceStatus', {
extend: 'Ext.data.Model',
fields: [
{
mapping: 'id',
name: 'id',
type: 'int'
},
{
mapping: 'name',
name: 'name',
type: 'string'
}
]
});
发票网格
Ext.define('MyApp.view.ApplicationViewport', {
extend: 'Ext.container.Viewport',
requires: [
'MyApp.view.ClearTriggerField'
],
layout: {
type: 'border'
},
initComponent: function() {
var me = this;
Ext.applyIf(me, {
items: [
{
xtype: 'header',
region: 'north',
height: 100,
items: [
{
xtype: 'image',
height: 100,
width: 250,
alt: 'Logo',
src: 'images/logo.gif',
title: 'Logo'
}
]
},
{
xtype: 'container',
region: 'center',
layout: {
type: 'card'
},
items: [
{
xtype: 'container',
width: 150,
layout: {
type: 'border'
},
items: [
{
xtype: 'gridpanel',
collapseMode: 'mini',
region: 'west',
split: true,
autoRender: false,
maxWidth: 300,
width: 250,
bodyBorder: false,
animCollapse: false,
collapsed: false,
collapsible: true,
hideCollapseTool: true,
overlapHeader: false,
titleCollapse: true,
allowDeselect: true,
columnLines: false,
forceFit: true,
store: 'ClientDataStor',
dockedItems: [
{
xtype: 'toolbar',
dock: 'top',
items: [
{
xtype: 'cleartrigger'
},
{
xtype: 'tbfill'
},
{
xtype: 'button',
icon: '/images/settings.png'
}
]
}
],
columns: [
{
xtype: 'templatecolumn',
tpl: [
'<img class="pull-left client-menu-image" src="/images/{type}.png"><div class="client-menu-name">{name}</div><div class="client-menu-type">{type}</div>'
],
dataIndex: 'id',
text: 'Client'
}
],
selModel: Ext.create('Ext.selection.RowModel', {
}),
plugins: [
Ext.create('Ext.grid.plugin.BufferedRenderer', {
})
]
},
{
xtype: 'gridpanel',
region: 'center',
title: 'Invoices',
titleCollapse: false,
forceFit: true,
store: 'Invoice',
columns: [
{
xtype: 'numbercolumn',
maxWidth: 120,
minWidth: 50,
dataIndex: 'id',
groupable: false,
lockable: true,
text: 'ID',
tooltip: 'Invoice ID',
format: '0'
},
{
xtype: 'numbercolumn',
hidden: true,
maxWidth: 120,
minWidth: 50,
dataIndex: 'client_id',
groupable: true,
text: 'Client ID',
format: '0'
},
{
xtype: 'gridcolumn',
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
return record.getStatus().get('name');
},
maxWidth: 200,
minWidth: 100,
dataIndex: 'invoice_status_id',
text: 'Status'
},
{
xtype: 'datecolumn',
maxWidth: 200,
minWidth: 100,
dataIndex: 'issue_date',
text: 'Issue Date',
format: 'd M Y'
},
{
xtype: 'datecolumn',
maxWidth: 200,
minWidth: 100,
dataIndex: 'due_date',
text: 'Due Date',
format: 'd M Y'
},
{
xtype: 'datecolumn',
maxWidth: 200,
minWidth: 100,
dataIndex: 'payment_date',
text: 'Payment Date',
format: 'd M Y'
},
{
xtype: 'templatecolumn',
summaryType: 'sum',
maxWidth: 150,
minWidth: 50,
tpl: [
'${amount}'
],
defaultWidth: 80,
dataIndex: 'amount',
groupable: true,
text: 'Amount'
}
],
features: [
{
ftype: 'grouping'
}
]
}
]
}
]
}
]
});
me.callParent(arguments);
}
});
答案 0 :(得分:10)
我设法通过使用回调函数来使关联查找工作,但发现自己只是简单地从商店进行查找更容易。
我将代理从InvoiceStatus商店移到了InvoiceStatus模型上,并使InvoiceStatus商店自动加载。
我更改了Status列的render方法,以便像这样从InvoiceStatus商店中查找显示名称。
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
var store = Ext.data.StoreManager.lookup('InvoiceStatus');
return store.getById(value).get('name');
},
这被证明是一个非常简单的解决方案。
答案 1 :(得分:3)
您似乎需要在belongsTo
子模型上设置InvoiceStatus
关联。您会认为在一个方向上定义关联会自动在另一个方向上创建关联,但显然情况并非如此,您必须在父级和子级上定义关联。有关更详细的说明,请参见此处:Why isn't my ExtJS Store Association Working
答案 2 :(得分:3)
你的hasOne应该是这样的:
hasOne: {
name: 'status',
instanceName: 'status',
associationKey: 'status',
model: 'MyApp.model.InvoiceStatus',
foreignKey: 'invoice_status_id',
getterName: 'getStatus',
setterName: 'setStatus'
}
ExtJS 4.2.2的以下补丁将允许您设置dataIndex: 'status.name'
而无需任何其他渲染器。网格将显示一切正常但排序不起作用。
此解决方案不适用于异步(懒惰)状态加载。
Ext.view.Table.prototype.renderCell = function(column, record, recordIndex, rowIndex, columnIndex, out) {
var me = this,
selModel = me.selModel,
cellValues = me.cellValues,
classes = cellValues.classes,
// fieldValue = record.data[column.dataIndex]; // patched
fieldValue = null,
cellTpl = me.cellTpl,
fullIndex, value, clsInsertPoint;
// Patch start
if (column.dataIndex && column.dataIndex.indexOf('.') > 0) {
var associationParts = column.dataIndex.split('.'),
v = record;
for (var i = 0; i < associationParts.length-1; i++) {
v = v['get' + associationParts[i].charAt(0).toUpperCase() + associationParts[i].slice(1)]();
}
fieldValue = v.get(associationParts[associationParts.length-1]);
}
else {
fieldValue = record.data[column.dataIndex];
}
// Patch end
cellValues.record = record;
cellValues.column = column;
cellValues.recordIndex = recordIndex;
cellValues.rowIndex = rowIndex;
cellValues.columnIndex = columnIndex;
cellValues.cellIndex = columnIndex;
cellValues.align = column.align;
cellValues.tdCls = column.tdCls;
cellValues.innerCls = column.innerCls;
cellValues.style = cellValues.tdAttr = "";
cellValues.unselectableAttr = me.enableTextSelection ? '' : 'unselectable="on"';
if (column.renderer && column.renderer.call) {
fullIndex = me.ownerCt.columnManager.getHeaderIndex(column);
value = column.renderer.call(column.scope || me.ownerCt, fieldValue, cellValues, record, recordIndex, fullIndex, me.dataSource, me);
if (cellValues.css) {
// This warning attribute is used by the compat layer
// TODO: remove when compat layer becomes deprecated
record.cssWarning = true;
cellValues.tdCls += ' ' + cellValues.css;
delete cellValues.css;
}
} else {
value = fieldValue;
}
cellValues.value = (value == null || value === '') ? ' ' : value;
// Calculate classes to add to cell
classes[1] = column.getCellId();
// On IE8, array[len] = 'foo' is twice as fast as array.push('foo')
// So keep an insertion point and use assignment to help IE!
clsInsertPoint = 2;
if (column.tdCls) {
classes[clsInsertPoint++] = column.tdCls;
}
if (me.markDirty && record.isModified(column.dataIndex)) {
classes[clsInsertPoint++] = me.dirtyCls;
}
if (column.isFirstVisible) {
classes[clsInsertPoint++] = me.firstCls;
}
if (column.isLastVisible) {
classes[clsInsertPoint++] = me.lastCls;
}
if (!me.enableTextSelection) {
classes[clsInsertPoint++] = me.unselectableCls;
}
if (cellValues.tdCls) {
classes[clsInsertPoint++] = cellValues.tdCls;
}
if (selModel && selModel.isCellModel && selModel.isCellSelected(me, recordIndex, columnIndex)) {
classes[clsInsertPoint++] = (me.selectedCellCls);
}
// Chop back array to only what we've set
classes.length = clsInsertPoint;
cellValues.tdCls = classes.join(' ');
cellTpl.applyOut(cellValues, out);
// Dereference objects since cellValues is a persistent var in the XTemplate's scope chain
cellValues.column = null;
};