PDA

View Full Version : ArrayStore Questions



parky128
15 Oct 2010, 12:02 AM
Hello,

I have an ArrayStore I am loading with local data as follows:


store = new Ext.data.ArrayStore({
fields: ['UntID','UnitName', 'Location','Lat','Lon', {name:'LastUpdateDTUTC', type: 'date'}, 'MapIcon'],
idIndex: 0
});

store.loadData([{UntID: 3450, UnitName: 'unitNameTest',Location: 'PInpointers',Lat:54.2100,Lon:-1.1542, LastUpdateDTUTC:'01/10/2010 23:10:22',MapIcon:10012},
{UntID: 3430, UnitName: 'unitRob',Location: 'Poole',Lat:54.2330,Lon:-1.1488, LastUpdateDTUTC:'05/10/2010 13:10:22',MapIcon:10020}]);

record = store.getById(3450);

The final line of code for me will not allow me to retrieve the record by the ID value I am passing in, even though I have configured the idIndex config property to zero which will be for the UntID values in my data array object. I just get null back, can someone help me please?

Animal
15 Oct 2010, 12:08 AM
Your data rows are not Arrays

This is one of your rows:



{
UntID: 3450,
UnitName: 'unitNameTest',
Location: 'PInpointers',
Lat: 54.2100,
Lon: -1.1542,
LastUpdateDTUTC: '01/10/2010 23:10:22',
MapIcon: 10012
}


(Looks like familiar data.... I've worked on an app like this)

parky128
15 Oct 2010, 12:31 AM
Familiar eh? Hope it wasn't one of our competitors! :-?

As for the data I am loading in, I see the problem and have now got it loading in using the following syntax...


store.loadData([[3450, 'unitNameTest','PInpointers',54.2100,-1.1542,'01/10/2010 23:10:22',10012],
[3430,'unitRob','Poole',54.2330,-1.1488, '05/10/2010 13:10:22',10020]]);

However I still cannot retrieve a record from the id value, e.g. 3450.

When inspecting the store object in Chrome's developer tool, I see the following structure:



data: Object

allowFunctions: false
events: Object
getKey: function (record) {
items: Array (2)

0: Object

data: Array (7)

0: 3450
1: "unitNameTest"
2: "PInpointers"
3: 54.21
4: -1.1542
5: "01/10/2010 23:10:22"
6: 10012
LastUpdateDTUTC: ""
Lat: ""
Location: ""
Lon: ""
MapIcon: ""
UnitName: ""
UntID: ""
length: 7











Why are the data values not paired up with the keys and just indexed instead, followed by the fields I defined earlier?

I ultimately want to load my ArrayStore from the server which returns the data in the same format:


{"success":true,"data":[[7924,"353241002514670","DISCOVERY COURT",50.75136,-1.92836,"\/Date(1263978338550+0000)\/",10001],[5900,"Cliooo (Nanny)","LG HQ",51.63839,-0.55491,"\/Date(1283416732947+0100)\/",10091],[8039,"Kevin Mclane","DISCOVERY COURT/OVER THERE",50.75135,-1.92860,"\/Date(1283423757663+0100)\/",10037],[8053,"Rob Parker","DISCOVERY COURT",50.75126,-1.92829,"\/Date(1279261079583+0100)\/",10091],[8055,"Jon Poper","LG HQ",51.63828,-0.55496,"\/Date(1284376393900+0100)\/",10001],[8052,"D Wright","DISCOVERY COURT",50.75137,-1.92836,"\/Date(1284552196717+0100)\/",10001],[8066,"Rob Sat Nav Testinger","DISCOVERY COURT",50.75136,-1.92837,"\/Date(1286798435053+0100)\/",10001],[8059,"PM Visit Test","DISCOVERY COURT",50.75139,-1.92836,"\/Date(1286877047450+0100)\/",10001],[7389,"AVG Sim rob","Hotwell Road (A4), City Of Bristol heading NW at 7mph",51.45055,-2.62500,"\/Date(1286880170610+0100)\/",10092]]}

When I try loading in the returned data into my ArrayStore, I get the same structure in Chrome as shown further above.

I don't get it, I have used this same approach in other apps using ArrayStores, loading in data remotely and they have worked as expected.

Animal
15 Oct 2010, 1:24 AM
{"success":true,"data":[


Well that needs a root config because the datablock returned is not an Array, so the reader needs to be told which property to look at to get the Array of rows.

I worked on a vehicle tracking app. The GPS units would report vehicle status every 2 minutes through the GSM network (SMS I think). They report speed, heading location, and even harsh braking and acceleration.

Each report is logged.

The Ext UI contains a map with little tooltips pointing at each vehicles location. Mouseovering a tip expands it into a little Panel containing vehicle info. if you watch it for a while the little tips blip across the map. It's very cool technology.

This is an old screenshot from the development phase. You can see I've mouseovered one of the unit markers and got the extra info:

http://i131.photobucket.com/albums/p286/TimeTrialAnimal/mapapp.jpg

parky128
15 Oct 2010, 1:49 AM
Dont suppose you know whose tracking app that was?

I actually work for a vehicle tracking company (www.pinpointers.com (http://www.pinpointers.com)) using very much the same technology you mention, we are looking at Ext-ifying our app as it would save us so much pain using other third party UI components which are a pain at times to get displaying\behaving consistently across the browsers.

Back to the code...

I'm using an ajax request to load in data to my store in the success handler as displayed below. The unitData variable is an object made up of my data and success objects coming back in response.responseText property in the handler:



Ext.decode(response.responseText): Object

data: Array (9)

0: Array (7)

0: 7924
1: "353241002514670"
2: "DISCOVERY COURT"
3: 50.75136
4: -1.92836
5: "/Date(1263978338550+0000)/"
6: 10001
length: 7
__proto__: Array


1: Array (7)
2: Array (7)
3: Array (7)
4: Array (7)
5: Array (7)
6: Array (7)
7: Array (7)
8: Array (7)
length: 9
__proto__: Array


success: true



My actual AJAX request code...


Ext.Ajax.request({
url: 'GetData.ashx',
success: function(response, opts) {

var unitData = Ext.decode(response.responseText);

store = new Ext.data.ArrayStore({
fields: ['UntID','UnitName', 'Location','Lat','Lon', {name:'LastUpdateDTUTC', type: 'date'}, 'MapIcon'],
idIndex: 0,
root: 'data'

});

store.loadData(unitData);

record = store.getById(3450);


},
failure: function(response, opts) {
//console.log('server-side failure with status code ' + response.status);
}
});

But, when I look in my Chrome tool the store is still not loading the data in as I would expect, i.e. no key\value pairings:



store: Object

data: Object

allowFunctions: false
events: Object
getKey: function (record) {
items: Array (2)

0: true
1: Array (9)

0: Array (7)

0: 7924
1: "353241002514670"
2: "DISCOVERY COURT"
3: 50.75136
4: -1.92836
5: "/Date(1263978338550+0000)/"
6: 10001
length: 7
__proto__: Array


1: Array (7)
2: Array (7)
3: Array (7)
4: Array (7)
5: Array (7)
6: Array (7)
7: Array (7)
8: Array (7)
length: 9
__proto__: Array


length: 2
__proto__: Array


keys: Array (2)

0: "success"
1: "data"

Animal
15 Oct 2010, 2:17 AM
I very strongly recommend that you split your app and structure it logically in an OO manner if it is going to grow.

The app I worked on has a full ExtJS single page interface for everything. Map view, reports, settings, creation of map pointers, geofences, alarms, SMS messages, Garmin messages. All in one page with a TabPanel.

This means that it really has to be structured.

You should define your business objects.

I really HATE that ArrayStore and JsonStore and I asked them to get rid of it because it hides the process of creating data objects.

You need to create your business object definitions (called "Records" in Ext 3, and "Models" in Ext 4). ArrayStore hides the fact that this is being done in that "fields" config, and means that you don't know that there is a reusable object definition. It actually generates a constructor for you which it uses, and which you can use to create record instances.

So



Ext.ns('my.namespace');
my.namespace.Unit = Ext.data.Record.create([
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]);


you might even like to create a custom VELatLong data type (Assuming it's a Bing Map, if a GMap, use another appropriate type)

http://dev.sencha.com/deploy/dev/docs/?class=Ext.data.Types

The business objects you intend to work with in the client should be defined in one source module.

Then, you should probably create a Store in another source module



Ext.ns('my.namespace');
my.namespace.unitStore = new Ext.data.Store({
url: 'GetData.ashx',
reader: new Ext.data.ArrayReader({
root: 'data',
idIndex: 0
}, my.namespace.Unit),
autoLoad: true,
listeners: {
load: function() {
// kick off the functionality of your app here after the store is loaded...
console.log(my.namespace.unitStor.getAt(0));
}
}
});

parky128
15 Oct 2010, 2:44 AM
Yeah we've developed other Ext based apps using the OO approach and will certainly be using the same technique when re-coding our tracking app.

As for this ArrayStore, I get an error in Chrome debugger suggesting there is no create method on the Ext.data.Record class:

Uncaught TypeError: Object #<an Object> has no method 'create'



var recordStruct = Ext.data.Record.create([
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]);

var unitStore = new Ext.data.Store({
url: 'GetData.ashx',
reader: new Ext.data.ArrayReader({
root: 'data',
idIndex: 0
}, recordStruct),
autoLoad: true,
listeners: {
load: function() {
}
}
});I have omitted to tell you that I am actually using the Touch API for this store code (sorry!), but couldn't post this thread on their forum as its closed for new posts! I didnt think it would matter posting here as this is more a general store based question anyway so thought it would be fine on this forum.

Even in the Touch API docs there is no entry for the Ext.data.Record class! Do you know if this means the technique you have suggested to me is not yet supported for Touch? If so, what a pain!

Animal
15 Oct 2010, 2:57 AM
Wrong forum then.

Well, you will create a Model in Sencha Touch (And ExtJS 4.0 when it's released):



Ext.ns('my.namespace');
my.namespace.Unit = Ext.regModel('Unit', {
fields: [
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]
});


Capitalize that name. It is a class definition

parky128
15 Oct 2010, 3:23 AM
Yeah like i say the Touch forum is closed for new posts for some reason!

Ok, I am trying the regModel way but still it doesnt change the overall outcome.



var recordStruct = Ext.regModel('Unit', {
fields:[
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]
});

var unitStore = new Ext.data.Store({
reader: new Ext.data.ArrayReader({
root: 'data',
idIndex: 0
}, recordStruct)

});

Ext.Ajax.request({
url: 'GetData.ashx',
success: function(response, opts) {

var unitData = Ext.decode(response.responseText);

unitStore.loadData(unitData);

record = store.getById(3450);


},
failure: function(response, opts) {
//console.log('server-side failure with status code ' + response.status);
}
});


Chrome Debugger output for unitStore object...



unitStore: Object

data: Object

allowFunctions: false
events: Object
getKey: function (record) {
items: Array (2)

0: true
1: Array (9)

0: Array (7)

0: 7924
1: "353241002514670"
2: "DISCOVERY COURT"
3: 50.75136
4: -1.92836
5: "/Date(1263978338550+0000)/"
6: 10001
length: 7
__proto__: Array

1: Array (7)
2: Array (7)
3: Array (7)
4: Array (7)
5: Array (7)
6: Array (7)
7: Array (7)
8: Array (7)
length: 9
__proto__: Array

length: 2
__proto__: Array

keys: Array (2)

0: "success"
1: "data"
length: 2






So still no key\value pairings. Does the ArrayReader constructor take a model definition as the second param or does it have to be a record definition?

Sorry, I know I seem to be going round the houses on this one, its just driving me mad!

Animal
15 Oct 2010, 3:33 AM
Load the Store. Don't do the Ajax yourself. Stores do that for you.

parky128
15 Oct 2010, 3:46 AM
OK...




onReady: function() {

var recordStruct = Ext.regModel('Unit', {
fields:[
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]
});

var unitStore = new Ext.data.Store({
url : 'GetData.ashx',
reader: new Ext.data.ArrayReader({
root: 'data',
idIndex: 0
}, recordStruct),
autoLoad: true

});

//rest of code below

}

If I break into my next block of code after setting up the unitStore In Chrome debugger I see no XHR call being made to the url configured in the store. I thought 'autoLoad: true' does this piece so like you say, no need to do the AJAX request part seperately.

I did try this earlier, but as above just doesnt load, hence why I tried the AJAX request way and load in the responseText in the success handler.

I must be doing something very stupid here.

Animal
15 Oct 2010, 4:14 AM
There must be some subtle thing going wrong.

Because that code is correct. autoLoad should fire off an Ajax load request immediately.

You will have to step into the constructor in the debugger, and follow it through until it [b]should[/b
step into an Ajax call.

Animal
15 Oct 2010, 4:15 AM
That code does not need to be (And in fact SHOULD NOT be) in an onReady.

That does not need any DOM to be there. The sooner you start off the load, the sooner you can start using the data.

Animal
15 Oct 2010, 4:19 AM
Sure you're not getting an error?

It needs an explicit proxy




var unitStore = new Ext.data.Store({
proxy: {
type: 'ajax',
url: 'GetData.ashx'
},
reader: new Ext.data.ArrayReader({
root: 'data',
idIndex: 0
}, recordStruct),
autoLoad: true

});

Animal
15 Oct 2010, 4:20 AM
Don't use "recordStruct" as the name of the class which encapsulates your "Unit" business object!

Use for example "Unit". It is a full class.

parky128
15 Oct 2010, 4:36 AM
Still no better even with the proxy defined, Chrome is not raising any error in the console window either.

The Touch forum is open for new posts again, so Im gonna post a link to this thread on there and see if they can assist any further if ok by you?

Thanks for the time you have spent on this already.

Animal
15 Oct 2010, 4:40 AM
Well this is interesting. Because I just pasted



var recordStruct = Ext.regModel('Unit', {
fields:[
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]
});

var unitStore = new Ext.data.Store({
proxy: { type: 'ajax', url: 'GetData.ashx'},
reader: new Ext.data.ArrayReader({
root: 'data',
idIndex: 0
}, recordStruct),
autoLoad: true

});


Into Chrome's console while running the Sencha Touch examples.

And it fired off an Ajax request immediately. Of course it 404'd, but it made the request.

parky128
15 Oct 2010, 5:03 AM
Ok, code now tweaked as per the Touch API docs example of a store configured with a proxy and inline reader, got a 'load' listener configured as well...


onReady: function() {

var map = new Ext.Map({
title: 'Map',
getLocation: true,
mapOptions: {
zoom: 12
}
});


var panel = new Ext.TabPanel({
fullscreen: true,
animation: 'slide',
items: [map]
});

//Load a data store from remote source so we can load some positions onto the map...

var Unit = Ext.regModel('Unit', {
fields:[
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]
});

var unitStore = new Ext.data.Store({
autoLoad: true,
model: 'Unit',
proxy: {
type: 'ajax',
url: 'GetData.ashx',
reader: {
type: 'array',
root: 'data'
}
},
listeners: {
'load': function() {
alert('loaded');
}
}

});

}

I can see the AJAX request now in chrome, data is being returned, but the store load event is not being fired if I try and break on my alert line.

So it suggests the load is failing, wish Chrome would tell me why!

parky128
15 Oct 2010, 6:16 AM
I see my problem, was trying to break into code immediately after defining the store and it hadnt kicked off the AJAX request at the point.

Anyway, I have discovered I have been a naughty boy...

I was not using the latest version of the Touch API, and after implementing this, things started to work a little better for me. Hardly surprising of course!

I've now re-worked my code to make it more OO friendly and end up with:


Ext.ns("PPMobi");

PPMobi.workspace = function() {
var unitStore, map, panel;

//TO-DO: Define this elsewhere
var Unit = Ext.regModel('Unit', {
fields:[
'UntID',
'UnitName',
'Location',
'Lat',
'Lon',
{name:'LastUpdateDTUTC', type: 'date'},
'MapIcon'
]
});

return {
init : function() {
this.buildStore();
},

buildStore: function() {

unitStore = new Ext.data.Store({
autoLoad: true,
model: "Unit",

proxy: {
type: 'ajax',
url : 'GetData.ashx',
reader: {
type: 'array',
root: 'data',
idIndex: 0
}
},
listeners: {
'load': function() {
this.buildUI()
}, scope:this
}
});
},

buildUI : function() {

map = new Ext.Map({
title: 'Map',
getLocation: true,
mapOptions: {
zoom: 12
}
});


panel = new Ext.TabPanel({
fullscreen: true,
animation: 'slide',
items: [map]
});

}

};
}();


Ext.onReady(PPMobi.workspace.init, PPMobi.workspace);


And if I break into my buildUI function and look at my store object in Chrome debugger, guess what I see?!



unitStore: Object

autoLoad: true
data: Object

allowFunctions: false
events: Object
getKey: function (record) {
items: Array (9)

0: Object

data: Object

LastUpdateDTUTC: null
Lat: 50.75136
Location: "DISCOVERY COURT"
Lon: -1.92836
MapIcon: 10001
UnitName: "353241002514670"
UntID: 7924













I get wonderful key\value pairings in my store now.

I feel very stupid indeed, but your help has been very much appreciated!!!

Thankyou very much

Animal
15 Oct 2010, 6:24 AM
Good start, you're on the road!