PDA

View Full Version : Adding new records to a store



BlueCamel
31 Aug 2009, 6:20 AM
I'm working with the reloading chart example as a proof of concept. I would like to add user selected data series to the chart. Essentially this means adding new data to the store and having the chart update.

I've modified the example to contain a grid. For this example, I'm assuming the user wants to add a 'visitors' data series to the existing graph and grid.



/*!
* Ext JS Library 3.0.0
* Copyright(c) 2006-2009 Ext JS, LLC
* licensing@extjs.com
* http://www.extjs.com/license
*/
function generateData(){
var data = [];
for(var i = 0; i < 12; ++i){
data.push([Date.monthNames[i],
(Math.floor(Math.random() * 11) + 1) * 100,
(Math.floor(Math.random() * 11) + 1) * 100
]);
}
return data;
}

// added function to generate some sample data for visits
function generateNewData(){
var data = [];
for(var i = 0; i < 12; ++i){
data.push([Math.floor(Math.random() * 11) + 1 * 100]);
}
return data;
}

Ext.BLANK_IMAGE_URL = '/code/ext-3.0.0/resources/images/default/s.gif';
Ext.onReady(function(){

// need to update the fields list to include the 'visits' field?
var store = new Ext.data.ArrayStore({
fields: ['month', 'hits', 'bytes'],
data: generateData()
});

// added column model for the grid;
// we'll have to add a new CM here for 'visits' dynamically
var cm = new Ext.grid.ColumnModel({
columns: [
{header: 'Month', sortable: true},
{header: 'Hits', sortable: true},
{header: 'Bytes', sortable: true}
]
});

// add code here to:
// 1) create the visitor sample data
// 2) add it to the store
// 3) update the grid (cm + refresh) and add chart data series

new Ext.Panel({
layout: 'border', //
width: 700,
height: 500,
renderTo: document.body,
title: 'Line Chart with Reload - Hits per Month',
tbar: [{
text: 'Load new data set',
handler: function(){
store.loadData(generateData());
}
}],
items: [{
xtype: 'linechart',
region: 'center',
margins: '2 2 0 2',
store: store,
xField: 'month',
series: [{
type: 'line',
displayName: 'Hits',
yField: 'hits'
},{
type: 'line',
displayName: 'Bytes',
yField: 'bytes'
}],
yAxis: new Ext.chart.NumericAxis({
displayName: 'Count',
labelRenderer: Ext.util.Format.numberRenderer('0,0')
}),
extraStyle: {
xAxis: {
labelRotation: -90
}
}
},{
xtype: 'grid',
region: 'south',
height: 200,
split: true,
collapsed: true,
collapsible: true,
cmargins: '2 2 2 2',
minSize: 200,
maxSize: 400,
floatable: false,
margins: '0 2 2 2',
store: store,
colModel: cm,
sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
frame: false,
title: 'Graph Values'
}]

});
});

BlueCamel
31 Aug 2009, 9:02 AM
So, continuing to poke at this. i'm not really adding a new record here, I think. I'm actually looking to extend existing records with a new field.

BlueCamel
31 Aug 2009, 11:03 AM
So, as documented when creating a record Ext.data.Fields are automatically by Ext.data.Record.create(). But there appears to be no way in the code to extend existing records in the store by appending or removing fields from the record.

I think this makes adding new columns to a grid impossible without reloading the entire store and then reconfiguring the grid. Thoughts?


/*
* @method create
* @return {function} A constructor which is used to create new Records according
* to the definition. The constructor has the same signature as {@link #Ext.data.Record}.
* @static
*/
Ext.data.Record.create = function(o){
var f = Ext.extend(Ext.data.Record, {});
var p = f.prototype;
p.fields = new Ext.util.MixedCollection(false, function(field){
return field.name;
});
for(var i = 0, len = o.length; i < len; i++){
p.fields.add(new Ext.data.Field(o[i]));
}
f.getField = function(name){
return p.fields.get(name);
};
return f;
};

BlueCamel
31 Aug 2009, 11:38 AM
Okay, found this too: http://extjs.com/forum/showthread.php?t=78433

So, based on code reading it doesn't look like ExtJS provides any methods for adding or removing fields on records in the store. It's expected that when the store is defined all fields will be defined as well. Or, that the field definition will be provided as a metaData in the case of a JsonReader.

jnicora
31 Aug 2009, 1:54 PM
For what it's worth, this is how I have changed fields within an existing store in the past. Not sure this would be a recommended way of doing it, however. I was doing this in the success method of an ajax request with a respnse that looked like:



{fields: [{name: "fieldOne"}], data:[["value"]]}


I did it this way because I'm using an ArrayStore and not a JsonStore. This is the routine to populate the new fields/data:



var json = Ext.decode(response.responseText);
// create new instance of reader with the correct fields schema
store.reader = new Ext.data.ArrayReader(json, json.fields);
// give the store a reference of the new recordType
store.recordType = Ext.data.Record.create(json.fields);
// rebuild the fields property off the new schema
store.fields = new Ext.util.MixedCollection(false, function(field){
return field.name;
});
store.fields.addAll(json.fields);
// rebuild data property based off the new data schema
store.data = new Ext.util.MixedCollection(false, function(field){
return field.name;
});
store.data.addAll(store.reader.readRecords(json.data).records);
// fire events that this store has loaded and updated metaData
store.fireEvent("load", store, store.data, store.lastOptions);

BlueCamel
31 Aug 2009, 2:19 PM
That final store.load() request looks like it will call the server side to request all records? I can see where this would work, but ideally we would request only the new field values and then walk the records in the store using record.set(newfield, newvalue).

Hmm, I'll look at this more. Thanks for the direction!

jnicora
31 Aug 2009, 2:26 PM
That final store.load() request looks like it will call the server side to request all records? I can see where this would work, but ideally we would request only the new field values and then walk the records in the store using record.set(newfield, newvalue).

Hmm, I'll look at this more. Thanks for the direction!

There would actually be no load or loadData method called at all in this case, the final call is to fire the load event. The data and/or fields in this case would need to be provided in another way, in my example I get it from an Ajax.request. This certainly does not follow the Ext model, but I did it so I could change fields for an array store. I don't see how you would be able to change the store fields (if there are existing rows of data) without re-loading new data in, because you need to update each of it's records with the new schema. This is also an "add all" method meaning you can't append fields, so to speak, but rather completely reconfigure them. I would actually love to hear a better way of doing this.