PDA

View Full Version : Calculated field in model



JavascriptParrot
4 Sep 2012, 5:25 AM
Hello all,

I have a model with different fields that are based on other fields. I thought I can use the convert function but that is only called at the first time. So it returns the value from the beginning en it's not calculated with changed data in the store. What kind of solutions are possible? Do I have to create a function in the model that update the calculated fields?



fields: [
{
name: 'FieldA'
},
{
name: 'FieldB' //editable in grid, default is 0
},
{
name: 'FieldC' // is A * B
convert: function(v, record){
return record.data.FieldA * record.data.FieldB; // FieldB is always 0 here
}
}
]

//maybe a function?
updateCalculatedFields: function() {
this.set('FieldC', this.get('FieldA') * this.get('FieldB') );
}


I hope somebody knows the trick or can give me some good advice.

Thanks!

sword-it
4 Sep 2012, 5:55 AM
Hi,

you can use following code:
you can use renderer to particular column



Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'a'},
{name: 'b'},
{name: 'c'},
]
});
Ext.create('Ext.data.Store', {
storeId:'simpsonsStore',
model:'User',
data:{'items':[
{ 'a': '2', "b":"5", "c":"0" },
{ 'a': '3', "b":"6", "c":"0" },
{ 'a': '4', "b":"5", "c":"0" },
{ 'a': '4', "b":"8", "c":"0" }
]},
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'items'
}
}
});


Ext.create('Ext.grid.Panel', {
title: 'Simpsons',
store: Ext.data.StoreManager.lookup('simpsonsStore'),
columns: [
{ text: 'A', dataIndex: 'a' },
{ text: 'B', dataIndex: 'b',editor:'textfield' },
{ text: 'C', dataIndex: 'c',renderer:function(value,metadata,record){
return parseInt(record.get('a'))*parseInt(record.get('b'));
} }
],
selType: 'rowmodel',
plugins: [
Ext.create('Ext.grid.plugin.RowEditing', {
clicksToEdit: 1
})
],
height: 200,
width: 400,
renderTo: Ext.getBody()
});

JavascriptParrot
4 Sep 2012, 6:01 AM
Thanks you for the fast response! =D>
I need also sum of the calculated fields, so a thought using Ext.getStore('store').sum('calculatedField');

In you're case this is not possible, I'm only showing the total of this calculated fields.
I hope you understand what I mean.

Thanks

scottmartin
4 Sep 2012, 6:22 AM
You can use converted fields:




Ext.onReady(function () {

Ext.define('TestResult', {
extend: 'Ext.data.Model',
fields: [
'student',
{ name: 'month1', type: 'int' },
{ name: 'month2', type: 'int' },
{
name: 'total', type: 'int',
convert: function(val,row) {
return row.data.month1 + row.data.month2;
}
}
]
});

var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1,
listeners: {
beforeedit: function(e,opts) {
//console.log(e);
console.log(opts); // opts.record.data.field to get field record value of data in row

},
edit: function(editor,e,opts){
console.log(e.value); // new value; update store; also see e.record object
}

}
});

Ext.create('Ext.grid.Panel', {
width: 500,
height: 140,
renderTo: document.body,
features: [{
ftype: 'summary'
}],
store: {
model: 'TestResult',
data: [{
student: 'Row 1',
month1: 100,
month2: 50
},{
student: 'Row 2',
month1: 200,
month2: 100
},{
student: 'Row 3',
month1: 300,
month2: 150
}
]
},
columns: [{
dataIndex: 'student',
text: 'Name',
summaryType: 'count',
summaryRenderer: function(value, summaryData, dataIndex) {
return Ext.String.format('{0} row{1}', value, value !== 1 ? 's' : '');
}
}, {
dataIndex: 'month1',
text: 'Month1',
summaryType: 'sum',
editor: { xtype: 'numberfield' }
}, {
dataIndex: 'month2',
text: 'Month2',
summaryType: 'sum',
editor: { xtype: 'numberfield' }
}, {
dataIndex: 'total',
text: 'total',
summaryType: 'sum'
}
],

plugins: [cellEditing]
});


});



Scott.

JavascriptParrot
4 Sep 2012, 9:54 AM
Thanks for the reply, I'm trying to using the convert function but I'm stuck. Here are some code snippets.

The model


{
name: 'Quantity',
type: 'int'
},
{
name: 'Total'
},
{
name: 'DensityInLiters'
},
{
name: 'StraightElementNNO3Percentage',
type: 'int'
},
{
name: 'StraightElementNNO3',
type: 'float',
convert: function(val, row){
return row.data.Quantity/1000*row.data.StraightElementNNO3Percentage;
}
},


And next the edit event function


onRawMaterialsGridEdit: function(editor, e, options) {
e.record.set('Total', (e.record.getData().Price / 1000) * e.record.getData().Quantity );
this.getRawMaterialsTotal().setValue(e.grid.store.sum('Total'));
console.log('Quantity: '+e.record.get('Quantity')); //output: 500
console.log('StraightElementNNO3Percentage: '+e.record.get('StraightElementNNO3Percentage')); //output:13%
console.log('StraightElementNNO3: '+e.record.get('StraightElementNNO3')); //output: 0
},

The convert function is not called when I user e.record.get so it returns the old value and that is zero. I think I forget something?

Thanks!

JavascriptParrot
4 Sep 2012, 10:25 PM
I'm still not succeeded, who knows what I'm doing wrong?

Thanks

vietits
4 Sep 2012, 11:15 PM
According to source code of Ext.data.Model, value of converted field will only be calculated and updated when you create a record from Model or set value to that field. So in your case, before getting value of converted field, you should set it to some value.


onRawMaterialsGridEdit: function(editor, e, options) {
e.record.set('Total', (e.record.getData().Price / 1000) * e.record.getData().Quantity );
this.getRawMaterialsTotal().setValue(e.grid.store.sum('Total'));
console.log('Quantity: '+e.record.get('Quantity')); //output: 500
console.log('StraightElementNNO3Percentage: '+e.record.get('StraightElementNNO3Percentage')); //output:13%
e.record.set('StraightElementNNO3');
console.log('StraightElementNNO3: '+e.record.get('StraightElementNNO3'));
},

JavascriptParrot
5 Sep 2012, 1:00 AM
Thank you! It works! \:D/=D>

JavascriptParrot
6 Sep 2012, 5:51 AM
Hello,

Unfortunately I have to reopen this thread because calculating the fields in the model is too slow.
I have a grid with one editable column after edit this column there are approximately 20 fields in the model that need to be updated. At this time one change takes 4 seconds in IE and 3 seconds in chrome... :s



//Controller, afteredit function
onRawMaterialsGridEdit: function(editor, e, options) {
e.record.updateCalculatedFields(); //update calculated fields in model, see below for model
this.getRawMaterialsTotal().setValue(e.grid.store.sum('Total')); //set the total to displayfield
}

//Model updateCalculatedFields
updateCalculatedFields: function(){
this.set('Total'); this.set('StraightElementNNO3'); this.set('StraightElementNNH4');
this.set('StraightElementNNH2'); this.set('StraightElementNtotal'); this.set('StraightElementP205');
this.set('StraightElementK2O'); this.set('StraightElementMgO'); this.set('StraightElementSO3');
this.set('StraightElementCl'); this.set('TraceElementFePPM'); this.set('TraceElementFePercentage');
this.set('TraceElementMnPPM'); this.set('TraceElementMnPercentage'); this.set('TraceElementZnPPM');
this.set('TraceElementZnPercentage');this.set('TraceElementCuPPM');
this.set('TraceElementCuPercentage');this.set('TraceElementBPPM');
this.set('TraceElementBPercentage');this.set('TraceElementMoPPM');
this.set('TraceElementMoPercentage');
}

//fields snippet
{
name: 'StraightElementNNO3Percentage',
type: 'int'
},
{
name: 'StraightElementNNO3',
type: 'float',
convert: function(val, row){
return row.data.Quantity/1000*row.data.StraightElementNNO3Percentage;
}
},
},


Is there anyway to speed this up? I hope someone knows the trick because I don't like to use server side calculation...

Thanks!

scottmartin
6 Sep 2012, 6:13 AM
because I don't like to use server side calculation...

I am not sure why you would make this statement.
My suggestion would be to create a view to calculate the data and simply return the result. The server's job is to manage the data and display the result to the client. Many slowdowns are created by trying to calculate data at the client during rendering.

This aside, You could try to create a function to calc the data and update the store before displaying it to the grid. I have not tried to benchmark this as I let the server do all of the work .. as it should.

Regards,
Scott.

JavascriptParrot
6 Sep 2012, 6:37 AM
I don't like to use server side calculation at this point because it is a concept and it should not take too much time. But I think you right, it's better to use the server side in this case.

Edit: I don't know if server side is better, one second sending, one second receiving and then updating store and painting grids?

The calculations are very simple, I find it strange that it take so much time.

Thanks

JavascriptParrot
6 Sep 2012, 9:18 AM
Hello,

Does it make sense when I use two stores? Now I have a grid, showing 4 columns from the store. After updating one field in the grid, the field is updated in the store. Then I update the calculating fields, the store is changed so the grid is refreshed (i think) but the calculated fields are not include in the grid. And when reloads the store/grid? After set all the calculated fields or after each calculated/changed field. In the last case it's better to use a store without a grid for the calculations. :-?

Thanks!

vietits
6 Sep 2012, 4:32 PM
updateCalculatedFields: function(){
this.set('Total'); this.set('StraightElementNNO3'); this.set('StraightElementNNH4');
this.set('StraightElementNNH2'); this.set('StraightElementNtotal'); this.set('StraightElementP205');
this.set('StraightElementK2O'); this.set('StraightElementMgO'); this.set('StraightElementSO3');
this.set('StraightElementCl'); this.set('TraceElementFePPM'); this.set('TraceElementFePercentage');
this.set('TraceElementMnPPM'); this.set('TraceElementMnPercentage'); this.set('TraceElementZnPPM');
this.set('TraceElementZnPercentage');this.set('TraceElementCuPPM');
this.set('TraceElementCuPercentage');this.set('TraceElementBPPM');
this.set('TraceElementBPercentage');this.set('TraceElementMoPPM');
this.set('TraceElementMoPercentage');
}

Also, instead of setting value for each field, you should setting multiple fields.


updateCalculatedFields: function(){
this.set({
Total: null,
StraightElementNNO3: null,
StraightElementNNH4: null,
...
});
}

JavascriptParrot
7 Sep 2012, 5:27 AM
Thanks! This improves speed considerably! =D>
Is it possible to use store.sum for more than one field? I have a lot of sums, for each sum it loop trough the records. It would be faster when the loop is run once and return the sums in an object.

Thanks

vietits
7 Sep 2012, 5:36 AM
<store>.sum() only calculate sum for one field at a time, so you should write your own sum() to do sum for all fields you want.

JavascriptParrot
7 Sep 2012, 6:51 AM
I did but it doesn't make sense, for now I'm happy...
Now it's time for weekend.

Thanks

scottmartin
9 Jan 2014, 9:55 AM
Please see the following:

pc

arunmatics
9 Jan 2014, 11:36 AM
Hi Please reply me ASAP.



Thanks for your valuable reply. I attached screen sot here which one I need to wrte the code.
The user has entered the value in quantity column.

scottmartin
9 Jan 2014, 1:21 PM
Here is a quick change to make the grid editable and then it will calc the summary:

Just use the store same store then update the grid as follows:



var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit : 1
});

var editText = { xtype : 'textfield' };
var editNumb = { xtype : 'numberfield' };

Ext.create('Ext.grid.Panel', {
title : 'Simpsons',
store : Ext.data.StoreManager.lookup('simpsonsStore'),
selModel : {
selType: 'cellmodel'
},
columns : [
{ header : 'Name', dataIndex : 'name', editor : editText },
{ header : 'Email', dataIndex : 'email', flex : 1, editor : editText },
{ header : 'Change', dataIndex : 'change', editor : editNumb, summaryType: 'sum' }
],
height : 200,
width : 400,
renderTo : Ext.getBody(),

features: [{
ftype: 'summary'
}],

plugins : [ cellEditing ]

});

scottmartin
9 Jan 2014, 3:30 PM
Here is another starting point. I do believe I have ran into a summary bug to where it is not working correctly on calculated field (convert) as the summary for the total field is not updating.

I will have to investigate a bit more.



// main app
Ext.onReady(function () {

var store = Ext.create('Ext.data.Store', {
fields: [
'student',
{ name : 'quantity', type : 'int' },
{ name : 'month1', type : 'int' },
{ name : 'month2', type : 'int' },
{
name : 'total', type : 'int',
convert: function(val, record) {
var qty = (record.data.quantity > 0) ? record.data.quantity : 1;
return (qty * (record.data.month1 + record.data.month2));
}
}
],

data: { 'items' : [
{
student : 'Row 1',
quantity : 1,
month1 : 100,
month2 : 50
},{
student : 'Row 2',
quantity : 1,
month1 : 200,
month2 : 100
},{
student : 'Row 3',
quantity : 1,
month1 : 300,
month2 : 150
}
]},

proxy : {
type : 'memory',
reader : {
type : 'json',
root : 'items'
}
}
});

var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit : 1,
listeners : {
edit : function(editor, e, eOpts){
e.record.set('total'); // make sure convert field is calculated :: EXTJSIV-5137
}
}
});

var editText = {
xtype : 'textfield'
};

var editNumb = {
xtype : 'numberfield',
minValue : 0
};

var grid = Ext.create('Ext.grid.Panel', {
width: 600,
height: 140,
renderTo: document.body,

store: store,

columns: [{
dataIndex: 'student',
text: 'Name',
summaryType: 'count',
summaryRenderer: function(value, summaryData, dataIndex) {
return Ext.String.format('{0} row{1}', value, value !== 1 ? 's' : '');
}
}, {
dataIndex : 'quantity',
text : 'Quantity',
editor : editNumb
}, {
dataIndex : 'month1',
text : 'Month1',
summaryType : 'sum'
}, {
dataIndex : 'month2',
text : 'Month2',
summaryType : 'sum'
}, {
dataIndex : 'total',
text : 'total',
summaryType : 'sum'
}],

features : [{
ftype : 'summary'
}],

plugins : [ cellEditing ],

});

});

arunmatics
10 Jan 2014, 9:39 AM
It works fine. I changed according to my stuff.

in that is there any possible to display some static rows in between 10 rows at after 5 rows ?

Thanks,
Arun Kumar

scottmartin
10 Jan 2014, 10:19 AM
You can display a row in the grid with different attributes using getRowClass:

As seen here:
http://ext4all.com/post/how-to-change-grid-row-background-color

Disable selection:


grid.on('beforeselect', function(view, record, item, index, node, e) {
return false; // disable select based on condition
});