ExtJS really shines with data models and its ease to extend its functionality
Tags: Coldfusion, extJS
Over the last couple of days I have been working on a very small research project, the idea was to just to get familiar with phoneGap and ExtJS Touch. But before I worked on the mobile side of the code, I wanted to just get familiar with the new data model in ExtJS 4.0.
I had been reading about this new feature for awhile now, and wanted to try reading a stream of xml data and store it into the data model. The first attempt the XML was just dummy nodes, with an array of children and it worked very well. So after getting the hang of this I began placing the dummy data with the real data, and discovered that the power of ExtJS is beyond imagination.
So lets take a look at how the new data model works.
ExtJS uses data stores for keeping records of data together, in a nice easy way to get and retrieve this information. The new Data Model takes this a step further by creating models that can have associations, this makes mapping data a lot easier.
So lets first take a look at a data model.
This is a user model defined to extend the Data Model class, and has fields that are defined as an int and a string. Which means we could write code to do something like this.
Which creates a user called Ed Spencer with an Age of 25, and now we have a defined user class.
So that is the basics of the Data Model, there is a lot more to it than this as we are about to see. Now my first problem to over come was the fact that I needed to get my XML data from another feed, so before I show you that solution lets have a look at how we can load this data locally first.
I opted to first go with creating a store and then telling the store that it has a data model to work with, and I will go into the data model a little later. So here is the first code that loads the data from my dummy file.
model : 'TeamStats',
proxy: {
type: 'ajax',
url : 'protocol.xml',
reader: {
type: 'teamstats',
root: 'xmlData',
record : 'stats'
}
},
autoLoad : false
});
This is a very basic store that is using a proxy of type ajax, and that we are going to go and grab the protocol.xml file locally, and that we are not going to auto load this data on creation. Now as this is XML data that is being retrieved we are also telling the reader, which is part of the ajax configuration) that we want to use a defined reader to do our work. More on this later.
For now I would like to mention that the reader config, we have said that we are defining the root and the record we want for the data model to use.
The data Model for this is
extend : 'Ext.data.Model',
fields : [
{ name : 'name', type : 'string', mapping : '@team' }
]
});
What we have defined here is a model called TeamStats, that will contain the field name and it will be linked to the attribute @team.
When we look at our xml data we will notice how this is going to be mapped, first the data.
If you remember when we defined the store, we told it to look for stats inside the xmlData node. So when it is stored in the data model we set up we will have two records, and the team will be set to the value defined. So we have been able to map the first part with ease, but the next part was a little tricky to get to work. After playing around with it for awhile I got stumped, and resorted to seeking a better understanding of how the data model works.
The problem was trying to map the next bit which is the player information, which is easy enough to setup in our data model. But getting the data in there was tricky, and required some ExtJS magic to rescue.
First lets get the new data models setup.
extend : 'Ext.data.Model',
fields : [
{ name : 'id', type : 'string', mapping : '@id' },
{ name : 'name', type : 'string', mapping : '@name' },
{ name : 'shots', type : 'string', mapping : '@shots' }
],
belongsTo : 'TeamStats'
});
Ext.define('TeamStats', {
extend : 'Ext.data.Model',
fields : [
{ name : 'name', type : 'string', mapping : '@team' }
],
hasMany : {model: 'Player', name: 'getPlayers', associationKey : 'players', reader: 'playerass'}
});
The very first thing I want to draw your attention to is the addition of the hasMany property that I have now added, which we have defined that we are going to have many players. And that the data model will be accessed by calling getPLayers() and will be associated with the key players.
Now the problem I was having was getting any of the data to map to the players model, this is were the rescue comes in. You can see that we are defining a reader called playerass, which will define how the data will be read.
And looks like this.
extend : 'Ext.data.reader.Xml',
alias : 'reader.playerass',
record : 'player'
});
And this is the magic, we are now telling the reader that we are going to extend the XML Reader. We are also going to define an alias for our reader that we can reference from other places. But the important thing to note here is that we are defining the record, to match the XML node.
However with this in place we still have one more trick to work. If we go back to the store for a minute you might recall that we set the reader type to teamstats, that is because we wanted to use a custom reader for here as well.
And the reader for this is defined as follows.
extend : 'Ext.data.reader.Xml',
alias : 'reader.teamstats',
record : 'stats',
root: 'xmlData'
,getAssociatedDataRoot:function(data, associationName) {
if(associationName == 'players')
return data;
else
return this.callParent(arguments);
}
});
This reader is a little interesting and I will admit that I don't fully understand what is happening here, but what I can see is that it is basically seeing if the associated data root is players or not. But without this it won't work correctly.
So the exercise was a success overall, it took a lot of research to get that understanding of how this fits together. The one thing that it demonstrates is the ease of which you can extend ExtJS, to provide something with extra functionality.
Now before I forget this still is not getting the data from a remote site, and we are not able to get an XML under normal circumstances and we need to use a proxy to do this for us. So if we go back to the store for a minute, and make a few changes so that the proxy looks like this.
url: 'http://domainlocation.com/protocol.xml',
reader: {
type: 'TeamStats',
root: 'xmlData',
record : 'stats'
read: function(response) {
var data = stringToXML(response.data);
if (response && response.responseText) {
data = this.getResponseData(response);
}
if (data) {
return this.readRecords(data);
} else {
return this.nullResultSet;
}
}
}
}),
With this problem we had to go into the classes and have a look at how we can solve this problem, the problem is that when we use this url it comes back as a wrapped XML string. Which means that we need to parse this somehow, and has you can see we found the solution by overriding the read method. Which allowed us to then convert this to an XML document, by using another function to do this for us.
The rest of the code for the reader is the same as it would have been, all we have done here is said that we want to convert the data to XML prior to ExtJS then taking further processing.
Now the one thing that I found here is that I didn't spend time writing code to actually parse the XML and manually have to store this into the data model. I was almost heading down that road before I seeked out help on this.
-
Andy,
Great stuff. I'm digging in deep now for the next book, and the data bits have always been one of my key points. I haven't tested yet, but I'm told the CFQueryReader is still good as a custom data reader for CF Query JSON returns too.# Posted By Steve 'Cutter' Blades | 5/18/11 12:11 PM -
Interesting stuff Andy. I hadn't tried accessing XML using a ScriptTagProxy before (indeed STP has been renamed to Ext.data.proxy.JsonP in 4.x). Did this work for you? It might make a good example for the framework itself if others find it useful too
# Posted By Ed Spencer | 5/18/11 1:20 PM -
Thanks Steve, I had a bit of help on the ExtJS forums and this is a result from that discussion.
# Posted By Andrew Scott | 5/18/11 5:10 PM -
Actually Ed no I didn't, might be worth a look at further.
# Posted By Andrew Scott | 5/18/11 5:11 PM -
Hi,
First thing, good explanation, but : alias does not work at all !
After I try to directly create my reader and i got lot of error ("Uncaught TypeError: Cannot read property 'model' of undefined", "The following classes are not declared even if their files have been loaded: 'charting.store.Series'. Please check the source code of their corresponding files for possible typos: './applications/charting/store/Series.js'").
Sound bad !
Why the alias is not working ?
And after all, why the Ext.create('charting.reader.SerieReader') causes those loading error ?
(i've check typos, path, requires, etc...)
Thx# Posted By Guillaume Chata | 2/8/12 4:03 AM -
I've found why :
Should add
Ext.require('charting.reader.SerieReader');
Ext.require('charting.reader.PointReader');
Ext.define('charting.controller.Charting',{
extend : 'Altair.abstract.Controller',
...
});
in my Charting.js file (which is a controller called dynamically into a desktop environment).
Now everything works well =D# Posted By Guillaume Chata | 2/8/12 4:20 AM



TweetBacks