PDA

View Full Version : Complex/Nested XML data associations don't work



tdack
29 Jul 2011, 8:18 PM
Sencha Touch version tested:

1.1.0

only default ext-all.css


Platform tested against:

iOS 4
Safari 5.1 (7534.48.3)


Description:

Models with associations (hasMany, belongsTo) do not get populated when the source data is nested XML


Test Case:

XML Source:


<GetProgramGuideResponse>
<StartTime>2011-07-28T1200</StartTime>
<EndTime>2011-07-29T1200</EndTime>
<StartChanId>1002</StartChanId>
<EndChanId>1100</EndChanId>
<NumOfChannels>18</NumOfChannels>
<Details>1</Details>
<Count>667</Count>
<AsOf>2011-07-30T12:20:09</AsOf>
<Version>0.24.20110505-1</Version>
<ProtoVer>63</ProtoVer>
<ProgramGuide>
<Channels>
<Channel inputId="0" chanFilters="" commFree="0" channelName="ABC1" sourceId="0" chanId="1002" chanNum="2" callSign="ABC1">
<Program title="Teenage Kicks" subTitle="Exodus" programFlags="4096" category="comedy" fileSize="0" seriesId="CRID://melbourne2.abc.net.au/248515" hostname="" catType="tvshow" programId="CRID://melbourne2.abc.net.au/248515e1267203" repeat="1" stars="0" endTime="2011-07-28T02:35:00" startTime="2011-07-28T02:11:00" lastModified="2011-07-28T02:11:00">
After annoying David, Milly and Max, the kids finally throw Vernon out. Bryan's missus won't let him stay at Bryan's either so Vernon ends up on the streets.
</Program>
<Program title="ABC News" subTitle="" programFlags="0" category="News" fileSize="0" seriesId="CRID://melbourne2.abc.net.au/100662" hostname="" catType="tvshow" programId="CRID://melbourne2.abc.net.au/100662e2606775" repeat="0" stars="0" endTime="2011-07-28T09:30:00" startTime="2011-07-28T09:00:00" lastModified="2011-07-28T09:00:00">
The latest news, breaking news and live coverage of events as they happen from Australia's largest broadcast news network. Comprehensive coverage, original reporting, and analysis of news in Australia and around the world.
</Program>
</Channel>
</Channels>
</ProgramGuide>
</GetProgramGuideResponse>


Model:


Ext.override( Ext.data.XmlReader, {
/*
Override required to enable XmlReader to return attributes from XML data and
also to allow contents of XML node to be returned as a value
eg:
<key1 property="foo">hello there</key1>

fields: [{name: 'key1', mapping: '$self'},
{name: 'key1-property', mapping: '@property'}]

*/
createAccessor: function() {
var selectValue = function(key, root, defaultValue){
if (key === '$self') {
var node = root;
if (node && node.firstChild) {
val = node.firstChild.nodeValue;
}
return Ext.isEmpty(val) ? defaultValue : val;
}

if( key == '#' ){
return root.tagName;
}
if( key.indexOf( '@' ) != -1 ){
var property = key.split( '@' )[ 1 ];
key = key.split( '@' )[ 0 ];
}
var val;
if( key.length ){
var node = Ext.DomQuery.selectNode(key, root);
if( node && node.firstChild ){
node = node.firstChild;
}
}
else{
var node = root;
}
if(node){
if( typeof( node.getAttribute ) != 'undefined' && typeof( property ) != 'undefined' ){
val = node.getAttribute( property );
}
else{
val = node.nodeValue;
}
}
return Ext.isEmpty(val) ? defaultValue : val;
};

return function(key) {
var fn;

if (key == this.totalProperty) {
fn = function(root, defaultValue) {
var value = selectValue(key, root, defaultValue);
return parseFloat(value);
};
}

else if (key == this.successProperty) {
fn = function(root, defaultValue) {
var value = selectValue(key, root, true);
return (value !== false && value !== 'false');
};
}

else {
fn = function(root, defaultValue) {
return selectValue(key, root, defaultValue);
};
}

return fn;
};
}(),
});

Ext.regModel('ChannelData',{
idProperty: 'chanId',
fields: [
{name: 'chanId', mapping: '@chanId', type: 'integer'},
{name: 'inputId', mapping: '@inputId', type: 'integer'},
{name: 'channelName', mapping: '@channelName', type: 'string'},
{name: 'chanNum', mapping: '@chanNum', type: 'string'},
{name: 'callSign', mapping: '@callSign', type: 'string'}
],
hasMany: {model: 'ProgramInformation', name: 'Program'},

proxy: {
type: 'ajax',
url: 'data.xml',
reader: {
type: 'xml',
root: 'Channels',
record: 'Channel'
}
}
});

Ext.regModel('ProgramInformation',{
fields: [
{name: 'title', mapping: '@title', type: 'string'},
{name: 'subtitle', mapping: '@subTitle', type: 'string'},
{name: 'category', mapping: '@category', type: 'string'},
{name: 'startTime', mapping: '@startTime', type: 'string'},
{name: 'endTime', mapping: '@endTime', type: 'string'},
{name: 'repeat', mapping: '@repeat', type: 'boolean'},
{name: 'description', mapping: '$self', type: 'string'},
],
belongsTo: 'ChannelData',
proxy: {
type: 'memory',
reader: {
type: 'xml',
root: 'Channel',
record: 'Program'
}
}
});

channelStore = new Ext.data.Store({
model: 'ChannelData',
sorters: 'chanNum'
});


See this URL : http://home.tdack.com/mvc/list


Steps to reproduce the problem:

execute the load() method for the store

channelStore.load();


The result that was expected:

Store loads the nested data


The result that occurs instead:

Store does not load the nested data and throws an exception:

TypeError: 'undefined' is not an object (evaluating 'root.length') sencha-touch-debug-w-comments.js:15523
Data for parent item (Channel) is not loaded


Screenshot or Video:

Safari Web Inspection just before the exception is thrown


Debugging already done:

Lots of stepping through Ext.data.Reader.readRecords with no joy
if the hasMany{name: 'Program'} (and it's case sensitive) does not match the nested xml tag you are trying to get the data from an exception is not thrown and a Store is not created for the nested items, but the parent items are loaded. So if the hasMany{name: } property does not match the xml tag you are trying to extract the nested data from then data association doesn't seem to happen.


Possible fix:

No idea sorry.


This bug may be considered a duplicate of http://www.sencha.com/forum/showthread.php?115033-FIXED-583-nested-xml-complex-xml-cant-get-associations-to-work - That bug is marked as [FIXED] but it seems it isn't working for everyone.