Questions:
What is the recommended way to filter a Ext.List populated with hierarchical data from a store? I need to filter out child objects that belongs to a parent object I have selected to filter on. The childs, in this case games should then populate the list.
What I need
- The Ext.List should be possible to filter by round, i.e. "Omgång 1", "Omgång 2" and so on. ("Omgång" = "Round" in Swedish) When selecting "Omgång 1" as filter the list should only display games from that round in the list. See JSON document below.
- The list should be grouped by date ("kickOff") and sorted by "gameId" ASC.
What I have done
- Created an Ext.List populated with data retrived via an Ext.data.Store that gets data from a JSON document read via ReST proxy.
- The Ext.List reads its data from the store Rounds (see below). The problem is that I only see one game for round "Omgång 1" when there should be 8 games.
This is what I have managed to achieve so far. The list is filtered using the buttons but only one of the list items are shown.
EM.model.Round
The model for Round. It has a one-to-many relationship with Match.
Ext.define('EM.model.Round', {
extend: 'Ext.data.Model',
init: function() {},
config: {
storeId: 'Rounds',
fields: [
'name',
'lockedDate',
],
associations: {
type: 'hasMany',
model: 'EM.model.Match',
primaryKey: 'gameId',
name: 'matches',
autoLoad: true,
associationKey: 'games'
}
},
});
EM.model.Match
The model for Match. It belongs to Round.
Ext.define('EM.model.Match', {
extend: 'Ext.data.Model',
init: function() {},
config: {
fields: [
{
name: 'gameId',
type: 'int'
},
{
name: 'firstTeam',
type: 'string'
},
{
name: 'firstTeamClass',
type: 'string',
convert: function(value, record) {
return util.convertFieldValueToLowerCase('firstTeam', record);
}
},
{
name: 'secondTeam',
type: 'string'
},
{
name: 'secondTeamClass',
type: 'string',
convert: function(value, record) {
return util.convertFieldValueToLowerCase('secondTeam', record);
}
},
'kickOff',
{
name: 'kickOffHour',
convert: function(value, record) {
var timestamp = new Date(util.convertUnixTimeToMilliseconds(record.get('kickOff')));
return Ext.Date.format(timestamp, 'H:i');
}
},
{
name: 'firstTeamGoals',
type: 'int',
defaultValue: 0
},
{
name: 'secondTeamGoals',
type: 'int',
defaultValue: 0
},
{
name: 'firstTeamGoalsBet',
},
{
name: 'secondTeamGoalsBet',
},
'points',
{
name: 'pointsEarned',
convert: function(value, record) {
var className = 'no-points-earned';
var points = record.get('points');
if (typeof points == 'undefined') {
return '';
}
if (points > 0) {
className = 'points-earned';
}
return '<div class="' + className + '">' + points + '</div>'
}
},
],
associations: {
type: 'belongsTo',
model: 'EM.model.Round',
name: 'round',
autoLoad: true
}
}
});
EM.store.Rounds
The store that reads the JSON document. This store is then used to populate the Ext.List.
Ext.define('EM.store.Rounds', {
extend: 'Ext.data.Store',
config: {
model: 'EM.model.Round',
storeId: 'Rounds',
filters: [{
property: 'name',
value: 'Round 1'
}],
/*grouper: {
groupFn: function (item) {
//var kickOff = new Date(util.convertUnixTimeToMilliseconds(item.get('kickOff')));
//return kickOff.format('d mmmm yyyy');
},
//sortProperty: 'kickOff'
},*/
proxy: {
type: 'rest',
url : 'resources/json/matches.json',
reader: {
type: 'json',
}
},
autoLoad: true,
}
});
JSON document
This is the JSON document that is read by the proxy in EM.store.Rounds.
[
{
"name": "Omgång 1",
"lockedDate": 1325420111,
"games": [
{
"gameId": 1,
"firstTeam": "Pol",
"secondTeam": "Gre",
"kickOff": 1339178400,
"firstTeamGoals": 0,
"secondTeamGoals": 3,
"firstTeamGoalsBet": 0,
"secondTeamGoalsBet": 3,
"points": 3
},
{
"gameId": 2,
"firstTeam": "Rus",
"secondTeam": "Cze",
"kickOff": 1339188300,
"firstTeamGoals": 4,
"secondTeamGoals": 1,
"firstTeamGoalsBet": 1,
"secondTeamGoalsBet": 2,
"points": 0
},{
"gameId": 3,
"firstTeam": "Ned",
"secondTeam": "Den",
"kickOff": 1339264800,
"firstTeamGoals": 2,
"secondTeamGoals": 1,
"firstTeamGoalsBet": 4,
"secondTeamGoalsBet": 2,
"points": 2
},
{
"gameId": 4,
"firstTeam": "Ger",
"secondTeam": "Por",
"firstTeamGoalsBet": 4,
"secondTeamGoalsBet": 0,
"kickOff": 1339274700
},
{
"gameId": 5,
"firstTeam": "Spa",
"secondTeam": "Ita",
"firstTeamGoalsBet": 3,
"secondTeamGoalsBet": 2,
"kickOff": 1339351200
},
{
"gameId": 6,
"firstTeam": "Irl",
"secondTeam": "Cro",
"kickOff": 1339361100
},
{
"gameId": 7,
"firstTeam": "Fra",
"secondTeam": "Eng",
"kickOff": 1339437600
},
{
"gameId": 8,
"firstTeam": "Ukr",
"secondTeam": "Swe",
"kickOff": 1339447500
}
]
},
{
"name": "Omgång 2",
"games": [
{
"gameId": 4,
"firstTeam": "Gre",
"secondTeam": "Cze",
"kickOff": 1339524000
}
]
},
{
"name": "Omgång 3",
"games": [
{
"gameId": 4,
"firstTeam": "Gre",
"secondTeam": "Rus",
"kickOff": 1339869600
}
]
},
{
"name": "Kvart",
"games": [
{
"gameId": 4,
"firstTeam": "1A",
"secondTeam": "2B",
"kickOff": 1340311500
}
]
},
{
"name": "Semi",
"games": [
{
"gameId": 4,
"firstTeam": "#25",
"secondTeam": "#27",
"kickOff": 1340829900
}
]
},
{
"name": "Final",
"games": [
{
"gameId": 4,
"firstTeam": "#29",
"secondTeam": "#30",
"kickOff": 1341175500
}
]
}
]
EM.view.MatchList
The list view that displays the list of matches.
Ext.define('EM.view.MatchList', {
extend: 'Ext.List',
xtype: 'matchlist',
requires: [
'Ext.TitleBar',
'EM.store.Rounds'
],
config: {
id: 'match-list',
store: 'Rounds',
//grouped: true,
scrollable: false,
items: [
{
xtype: 'titlebar',
scrollable: {
direction: 'horizontal',
directionLock: true
},
items: [
{
xtype: 'button',
text: 'Omgång 1',
handler: function() {
var sto = Ext.getStore('Rounds');
sto.clearFilter();
sto.filter('name', 'Omgång 1');
console.log(sto);
}
},
{
xtype: 'button',
text: 'Omgång 2',
handler: function() {
var sto = Ext.getStore('Rounds');
sto.clearFilter();
sto.filter('name', 'Omgång 2');
}
},
{
xtype: 'button',
text: 'Omgång 3',
handler: function() {
var sto = Ext.getStore('Rounds');
sto.clearFilter();
sto.filter('name', 'Omgång 3');
}
},
{
xtype: 'button',
text: 'Kvart',
handler: function() {
var sto = Ext.getStore('Rounds');
sto.clearFilter();
sto.filter('name', 'Kvart');
}
},
{
xtype: 'button',
text: 'Semi',
handler: function() {
var sto = Ext.getStore('Rounds');
sto.clearFilter();
sto.filter('name', 'Semi');
}
},
{
xtype: 'button',
text: 'Final',
handler: function() {
var sto = Ext.getStore('Rounds');
sto.clearFilter();
sto.filter('name', 'Final');
}
}
],
},
{
xtype: 'panel',
html: 'Senast uppdaterad: Idag kl 20:12'
}
],
itemTpl: [
'<div class="match-meta-data">',
'<tpl for="matches">',
'<div class="team-wrapper home-team">{firstTeam} <div class="flag {firstTeamClass}"><span></span></div> <span class="goals-scored">{firstTeamGoals}</span></div>',
'<div class="kick-off-time">{kickOffHour}</div>',
'<div class="team-wrapper away-team"><span class="goals-scored">{secondTeamGoals}</span> <div class="flag {secondTeamClass}"><span></span></div> {secondTeam}</div>',
'<div class="bet-meta-data">',
'<img class="user-icon" src="resources/images/user-22x26.png" />',
'<div class="home-team goals-bet">{firstTeamGoalsBet}</div>',
'<div class="away-team goals-bet">{secondTeamGoalsBet}</div>',
'{pointsEarned}',
'</div>',
'</tpl>',
'</div>',
].join('')
},
});
This is my first Sencha Touch app, so feel free to point out any bad practices that you see in the code. Could someone please provide an example of how to achieve what I'm aiming for? I have spent a lot of time trying to figure this one out.
The full project can be downloaded from GitHub at https://github.com/eriktoyra/EM-Tipset. Latest branch is _filter-match-list.
I managed to find a solution to this problem by using a test scenario that resembles the actual one I have.
Solution
What I did was:
The full solution is provided below and also available on GitHub.
I will keep the question as unanswered for some days if someone else should find a better solution or point out some improvements.
JSON data