EXT JS 4 uses model association to display grid display value

More details

I have a grid used to display account information. The grid is filled using the invoice store, the invoice model is used in the account store, the invoice model has "one connection" with the InvoiceStatus model with the primary key "id" and the foren key "invoice_status_id".

Problem

I'm not sure how to make the display value of the "Status" column of the "Use Associated Model Name" invoice inserted from invoice_status_id. I know that I need to create a visualization tool for this, but I still get a null value. Both instances of Invoice and InvoiceStatus are populated with the correct values.

Status column display

renderer: function(value, metaData, record, rowIndex, colIndex, store, view) { return record.getStatus().get('name'); }, 

Shop Accounts

 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 Store

 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)]); } }); 

Account Model

 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: 'Ym-d', dateReadFormat: '', mapping: 'issue_date', name: 'issue_date', sortType: 'asDate', type: 'date' }, { dateFormat: 'Ym-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 Model

 Ext.define('MyApp.model.InvoiceStatus', { extend: 'Ext.data.Model', fields: [ { mapping: 'id', name: 'id', type: 'int' }, { mapping: 'name', name: 'name', type: 'string' } ] }); 

Invoice grid

 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 MY' }, { xtype: 'datecolumn', maxWidth: 200, minWidth: 100, dataIndex: 'due_date', text: 'Due Date', format: 'd MY' }, { xtype: 'datecolumn', maxWidth: 200, minWidth: 100, dataIndex: 'payment_date', text: 'Payment Date', format: 'd MY' }, { xtype: 'templatecolumn', summaryType: 'sum', maxWidth: 150, minWidth: 50, tpl: [ '${amount}' ], defaultWidth: 80, dataIndex: 'amount', groupable: true, text: 'Amount' } ], features: [ { ftype: 'grouping' } ] } ] } ] } ] }); me.callParent(arguments); } }); 
+6
source share
3 answers

I managed to get the association search using the callback function, but it was much easier for me to just search from the store.

Step one

I moved the proxies from the InvoiceStatus repository to the InvoiceStatus model and autostarted the InvoiceStatus repository.

Second step

I changed the rendering method for the Status column to look up the display name from the InvoiceStatus repository, as it is.

 renderer: function(value, metaData, record, rowIndex, colIndex, store, view) { var store = Ext.data.StoreManager.lookup('InvoiceStatus'); return store.getById(value).get('name'); }, 

This turned out to be a very simple player solution.

+10
source

It looks like you need to set the belongsTo association in the child model of InvoiceStatus . You might think that defining an association in one direction would automatically create an association in the other direction, but apparently this is not so, and you should determine the relationship with both parents and children. See here for a more detailed explanation: Why my Store ExtJS association does not work

+3
source

Your hasOne should look like this:

 hasOne: { name: 'status', instanceName: 'status', associationKey: 'status', model: 'MyApp.model.InvoiceStatus', foreignKey: 'invoice_status_id', getterName: 'getStatus', setterName: 'setStatus' } 

The following patch for ExtJS 4.2.2 will allow you to set dataIndex: 'status.name' without any additional rendering. The grid will display everything in order, but sorting will not work.

This solution does not work with asynchronous (lazy) state loading.

 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 === '') ? '&#160;' : 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 scope chain cellValues.column = null; }; 
+3
source

All Articles