PDA

View Full Version : XML Nested List Example



danrosshoward
17 Aug 2012, 7:39 AM
I'm sure there is a better way to post this information, but the is the best way I know of for now... so, buckle up and hang on for an extremely long post.

I needed to use a nested list, based on XML data, but could not get it to work with Sencha Touch's nested list, so I rolled my own. Please improve it in any way you can.

The basic ingredients for this recipe are:

an xml file
a navigation view
one high level view (used as default in navigation view)
several lower level views
one high level store
several 'temporary' stores
several associated models
several 'temporary store' models
For the sake of simplicity, I did not use any controllers. Instead, I just used listeners.

Here are the files. There are 15 of them to make a three-tiered nested list.

app.js


Ext.application({
name: 'nestedXMLexample',
requires: [
'Ext.data.Store'
],
views: [
'TheNavigationView',
'HigherLevelListView',
'LowestTempListView',
'MiddleTempListView'
],
models: [
'TopLevelModel',
'LowestLevelModel',
'MiddleLevelModel',
'MiddleTemporaryStoreModel',
'LowestTemporaryStoreModel'
],
stores: [
'AssociatedModelsStore',
'MiddleTemporaryStore',
'LowestTemporaryStore'
],

launch: function() {

// Initialize the main view
Ext.Viewport.add(Ext.create('nestedXMLexample.view.TheNavigationView'));
},
});


index.html


<!DOCTYPE HTML>
<html manifest="" lang="en-US">
<head>
<meta charset="UTF-8">
<title>nestedXMLexample</title>
<style type="text/css">
/**
* Example of an initial loading indicator.
* It is recommended to keep this as minimal as possible to provide instant feedback
* while other resources are still being loaded for the first time
*/
html, body {
height: 100%;
background-color: #1985D0
}
#appLoadingIndicator {
position: absolute;
top: 50%;
margin-top: -15px;
text-align: center;
width: 100%;
height: 30px;
-webkit-animation-name: appLoadingIndicator;
-webkit-animation-duration: 0.5s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: linear;
}

#appLoadingIndicator > * {
background-color: #FFFFFF;
display: inline-block;
height: 30px;
-webkit-border-radius: 15px;
margin: 0 5px;
width: 30px;
opacity: 0.8;
}

@-webkit-keyframes appLoadingIndicator{
0% {
opacity: 0.8
}
50% {
opacity: 0
}
100% {
opacity: 0.8
}
}
</style>
<!-- The line below must be kept intact for Sencha Command to build your application -->
<script id="microloader" type="text/javascript" src="sdk/microloader/development.js"></script>
</head>
<body>
<div id="appLoadingIndicator">
<div></div>
<div></div>
<div></div>
</div></body>
</html>


structuredSampleXML.xml


<TopAlphaGrouper>
<TopAlphaItem TopAlphaItemAttributeOne="A">
<RomanGrouper>
<RomanItem RomanItemAttributeOne="I">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="a"/>
</LowerAlphaGrouper>
</RomanItem>
<RomanItem RomanItemAttributeOne="II">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="a"/>
<LowerAlphaItem LowerAlphaAttribute="b"/>
</LowerAlphaGrouper>
</RomanItem>
<RomanItem RomanItemAttributeOne="III">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="a"/>
<LowerAlphaItem LowerAlphaAttribute="b"/>
<LowerAlphaItem LowerAlphaAttribute="c"/>
</LowerAlphaGrouper>
</RomanItem>
<RomanItem RomanItemAttributeOne="IV">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="a"/>
<LowerAlphaItem LowerAlphaAttribute="b"/>
<LowerAlphaItem LowerAlphaAttribute="c"/>
<LowerAlphaItem LowerAlphaAttribute="d"/>
</LowerAlphaGrouper>
</RomanItem>
</RomanGrouper>
</TopAlphaItem>
<TopAlphaItem TopAlphaItemAttributeOne="B">
<RomanGrouper>
<RomanItem RomanItemAttributeOne="X">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="z"/>
<LowerAlphaItem LowerAlphaAttribute="y"/>
<LowerAlphaItem LowerAlphaAttribute="x"/>
<LowerAlphaItem LowerAlphaAttribute="j"/>
</LowerAlphaGrouper>
</RomanItem>
<RomanItem RomanItemAttributeOne="II">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="a"/>
<LowerAlphaItem LowerAlphaAttribute="b"/>
<LowerAlphaItem LowerAlphaAttribute="c"/>
<LowerAlphaItem LowerAlphaAttribute="d"/>
</LowerAlphaGrouper>
</RomanItem>
</RomanGrouper>
</TopAlphaItem>
<TopAlphaItem TopAlphaItemAttributeOne="C"/>
<TopAlphaItem TopAlphaItemAttributeOne="D">
<RomanGrouper>
<RomanItem RomanItemAttributeOne="XLII">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="alpha"/>
<LowerAlphaItem LowerAlphaAttribute="beta"/>
<LowerAlphaItem LowerAlphaAttribute="gamma"/>
<LowerAlphaItem LowerAlphaAttribute="delta"/>
</LowerAlphaGrouper>
</RomanItem>
<RomanItem RomanItemAttributeOne="MMXII">
<LowerAlphaGrouper>
<LowerAlphaItem LowerAlphaAttribute="This"/>
<LowerAlphaItem LowerAlphaAttribute="is"/>
<LowerAlphaItem LowerAlphaAttribute="the"/>
<LowerAlphaItem LowerAlphaAttribute="current"/>
<LowerAlphaItem LowerAlphaAttribute="year"/>
</LowerAlphaGrouper>
</RomanItem>
</RomanGrouper>
</TopAlphaItem>
<TopAlphaItem TopAlphaItemAttributeOne="E"/>
</TopAlphaGrouper>

TheNavigationView.js


Ext.define('nestedXMLexample.view.TheNavigationView', {
extend: 'Ext.navigation.View',
id: 'theNavigationViewId',
xtype: 'theNavigationViewXtype',
title: 'Navigation View Title',
requires: [
'nestedXMLexample.view.HigherLevelListView',
'nestedXMLexample.view.MiddleTempListView',
'nestedXMLexample.view.LowestTempListView'
],
config: {
items: [{
xtype:'higherLevelListXtype',
}],
listeners: {
back: function(){
console.log('back tapped');
console.log(this.getActiveItem().getStore());
var tempStore=Ext.getStore('middleTemporaryStoreId');
var lowTempStore=Ext.getStore('lowestMiddleTemporaryStoreId')
if(this.getActiveItem().getStore()==tempStore){
lowTempStore.setData("");
}
else{
tempStore.setData("");
}
console.log('store emptied');
}
}
}
});


HigherLevelListView.js


Ext.define("nestedXMLexample.view.HigherLevelListView", {
extend: 'Ext.List',
xtype: 'higherLevelListXtype',
config: {
fullscreen: true,
store: 'associatedModelsStoreId',
scrollable: true,
itemTpl: new Ext.XTemplate(
'<tpl for==".">
'<tpl if="TopAlphaItemAttributeOne==\'A\'"><div>{TopAlphaItemAttributeOne}</div><div style="color:red"><small> {TopLevelListOfChildren}</small></div></tpl>'+
'<tpl if="TopAlphaItemAttributeOne!=\'A\'"><div>{TopAlphaItemAttributeOne}</div><div style="color:green"><small> {TopLevelListOfChildren}</small></div></tpl>'+
'</tpl>'
),
listeners: {
itemtap: function(list, index, element, record){
console.log('item tapped in main view');
var assocModelStore=Ext.getStore('associatedModelsStoreId');
var tempStore=Ext.getStore('middleTemporaryStoreId');

assocModelStore.each(function(placeHolderForTopAlphaItem){
var listOfChildren="";
if(placeHolderForTopAlphaItem.get('TopAlphaItemAttributeOne')==record.get('TopAlphaItemAttributeOne')){
placeHolderForTopAlphaItem.RomanGrouper().each(function(RomanItem){
RomanItem.LowerAlphaGrouper().each(function(LowerAlphaItem){
listOfChildren+=LowerAlphaItem.get('LowerAlphaAttribute')+" ";
})
if(listOfChildren!=""){
listOfChildren=listOfChildren.slice(0,-1);
}
tempStore.add({
parentItemReference: placeHolderForTopAlphaItem.get('TopAlphaItemAttributeOne'),
tempFieldOne: RomanItem.get('RomanItemAttributeOne'),
listOfChildItems: listOfChildren
});
console.log(placeHolderForTopAlphaItem.get('TopAlphaItemAttributeOne')+
" "+
RomanItem.get('RomanItemAttributeOne')+
" "+
listOfChildren);
listOfChildren="";
});
}
});
console.log(record.get('TopLevelListOfChildren'));
if(record.get('TopLevelListOfChildren')!=""){
this.getParent().push({
xtype: 'middleTempListViewXtype',
title: 'temp List'

});
}
}
}
}
});


MiddleTempListView.js


Ext.define("nestedXMLexample.view.MiddleTempListView", {
extend: 'Ext.List',
xtype: 'middleTempListViewXtype',
config: {
fullscreen: true,
store: 'middleTemporaryStoreId',
scrollable: true,
itemTpl: new Ext.XTemplate(
'<div>{tempFieldOne}</div><div style="color:orange"><small>{listOfChildItems}</small></div>'

),
listeners: {
itemtap: function(list, index, element, record){
console.log('item tapped in MiddleTempListView');
console.log(element);
console.log(record);
var assocModelStore=Ext.getStore('associatedModelsStoreId');
var lowestTempStore=Ext.getStore('lowestMiddleTemporaryStoreId');
assocModelStore.each(function(placeHolderForTopAlphaItem){
placeHolderForTopAlphaItem.RomanGrouper().each(function(RomanItem){
RomanItem.LowerAlphaGrouper().each(function(LowerAlphaItem){
if(
placeHolderForTopAlphaItem.get('TopAlphaItemAttributeOne')
==
record.get('parentItemReference')
&&
RomanItem.get('RomanItemAttributeOne')
==
record.get('tempFieldOne')){
lowestTempStore.add({
grandparentItemReference: record.get('parentItemReference'),
parentItemReference: record.get('tempFieldOne'),
lowestTempFieldOne: LowerAlphaItem.get('LowerAlphaAttribute')
})
console.log(LowerAlphaItem.get('LowerAlphaAttribute'));
}
})
})
})
lowestTempStore.each(function(s){
console.log(s.get('grandparentItemReference'));

})
this.getParent().push({
xtype: 'lowestMiddleTempListViewXtype',
title: 'did it'
})
}
}
}
});


LowestLevelTempListView.js



Ext.define("nestedXMLexample.view.LowestTempListView", {
extend: 'Ext.List',
xtype: 'lowestMiddleTempListViewXtype',
config: {
fullscreen: true,
store: 'lowestMiddleTemporaryStoreId',
scrollable: true,
itemTpl: new Ext.XTemplate(
'{lowestTempFieldOne}'
)
}
});


TopLevelModel.js


Ext.define('nestedXMLexample.model.TopLevelModel', {
extend: 'Ext.data.Model',
config: {
fields: [
{name: 'TopAlphaItemAttributeOne', mapping:'@TopAlphaItemAttributeOne'},
'TopLevelListOfChildren'
],
hasMany: [
{model: 'nestedXMLexample.model.MiddleLevelModel', name: 'RomanGrouper', associationKey: 'RomanGrouper'}
]
}
});


MiddleLevelModel.js


Ext.define('nestedXMLexample.model.MiddleLevelModel', {
extend: 'Ext.data.Model',
config: {
fields: [
{name: 'RomanItemAttributeOne', mapping:'@RomanItemAttributeOne'},
],
hasMany:[
{model: 'nestedXMLexample.model.LowestLevelModel', name: 'LowerAlphaGrouper', associationKey: 'LowerAlphaGrouper'}
],
belongsTo: 'nestedXMLexample.model.TopLevelModel',
proxy: {
reader: {
type: 'xml',
record: 'RomanItem'
}
}
}
});


LowestLevelModel.js


Ext.define('nestedXMLexample.model.LowestLevelModel', {
extend: 'Ext.data.Model',
config: {
fields: [
{name: 'LowerAlphaAttribute', mapping:'@LowerAlphaAttribute'}
],
belongsTo: 'nestedXMLexample.model.MiddleLevelModel',
proxy: {
reader: {
type: 'xml',
record: 'LowerAlphaItem'
}
}
}
});


MiddleTemporaryStoreModel.js


Ext.define('nestedXMLexample.model.MiddleTemporaryStoreModel', {
extend: 'Ext.data.Model',
config: {
fields: [
'parentItemReference',
{name: 'tempFieldOne', type: 'string'},
'listOfChildItems'
]
}
});


LowestTemporaryStoreModel.js


Ext.define('nestedXMLexample.model.LowestTemporaryStoreModel', {
extend: 'Ext.data.Model',
config: {
fields: [
'grandparentItemReference',
'parentItemReference',
{name: 'lowestTempFieldOne', type: 'string'}
]
}
});

AssociatedModelsStore.js



Ext.define('nestedXMLexample.store.AssociatedModelsStore',{
extend: 'Ext.data.Store',
config: {
storeId: 'associatedModelsStoreId',
model: 'nestedXMLexample.model.TopLevelModel',
autoLoad: true,
defaultrootProperty: 'TopAlphaItem',
proxy: {
type: 'ajax',
url : 'structuredSampleXML.xml',
reader: {
type: 'xml',
record: 'TopAlphaItem'
}
},
listeners:{
load: (function(placeHolder){
placeHolder.each(function(placeHolderForTopAlphaItem){

var collectorString="";
placeHolderForTopAlphaItem.RomanGrouper().each(function(RomanItem){
collectorString+=RomanItem.get('RomanItemAttributeOne')+" ";
});
if(collectorString!=""){
collectorString=collectorString.slice(0,-1);
}
placeHolderForTopAlphaItem.set('TopLevelListOfChildren', collectorString);
});
})
}
}
});


MiddleTemporaryStore.js



Ext.define('nestedXMLexample.store.MiddleTemporaryStore',{
extend: 'Ext.data.Store',
config: {
storeId: 'middleTemporaryStoreId',
model: 'nestedXMLexample.model.MiddleTemporaryStoreModel',
}
});


LowestTemporaryStore.js


Ext.define('nestedXMLexample.store.LowestTemporaryStore',{
extend: 'Ext.data.Store',
config: {
storeId: 'lowestMiddleTemporaryStoreId',
model: 'nestedXMLexample.model.LowestTemporaryStoreModel',
}
});

mitchellsimoens
20 Aug 2012, 5:38 AM
Usually when posted examples, you should try to keep it simple. So to improve upon yours, I would just have the NestedList, store, model and the XML.

danrosshoward
21 Aug 2012, 5:36 AM
Thanks, Mitchell. I posted all of the code to make the example work, because I was a total noob just a little while ago, and really benefited from full examples that I could see in action. I should probably have put all of the code somewhere else, like github, but haven't tried doing something like that yet.

This isn't really a nested list, which I'm sure is obvious to you. It is a navigation view that behaves like a nested list. There is probably a better way to get a nested list to consume xml, but this is the only way I could get it to work.

Is there a better way?

I also included the ability to display information from the next level down in the current level of the nested list. I was trying to build an example that works like the nested list example by Drew Neil shown here:
http://vimeo.com/20580117

(http://vimeo.com/20580117)

00cmm
31 Aug 2012, 1:10 PM
Thanks, Mitchell. I posted all of the code to make the example work, because I was a total noob just a little while ago, and really benefited from full examples that I could see in action. I should probably have put all of the code somewhere else, like github, but haven't tried doing something like that yet.


I really appreciated this being a complete example. I am a total noob right now and this really helped me, even though it wasn't "best practice", it was the best thing for me right now.

Thanks again.

danrosshoward
4 Sep 2012, 4:39 AM
My pleasure. Thanks for your feedback.