PDA

View Full Version : Dynamically adding/removing 'grouped' & 'indexBar' to/from a List



shellgrit
16 Jul 2010, 2:20 AM
Is it possible to dynamically add/remove the config items 'grouped' and/or 'indexBar' from a List.

My list consists of shorebird species (ie. thumbnail, name & size) with toolbar buttons that the user can tap to sort the list alphabetically or by size. Grouped and an index bar would be great for the list sorted alphabetically, but not when it is sorted by size (unless it is possible to group/index it some other way).

Neil

evant
18 Jul 2010, 9:59 PM
Turning off the index bar is reasonably trivial, grouping a bit less so.



Ext.override(Ext.List, {
setGrouped: function(grouped){
this.grouped = !!grouped;
this.tpl = this.grouped ? this.groupTpl : this.itemTpl;
if(this.rendered){
if(!this.grouped){
this.el.addClass('x-list-flat');
if (this.indexBar) {
this.indexBar.hide();
}
}else{
this.el.removeClass('x-list-flat');
if(this.indexBar){
this.indexBar.show();
}
}
this.refresh();
}
}
});

Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady : function() {
Ext.regModel('Contact', {
fields: ['firstName', 'lastName']
});

var groupingBase = {
tpl: '<tpl for="."><div class="contact"><strong>{firstName}</strong> {lastName}</div></tpl>',
itemSelector: 'div.contact',

singleSelect: true,
grouped: true,
indexBar: true,

store: new Ext.data.Store({
model: 'Contact',
sorters: 'firstName',

getGroupString : function(record) {
return record.get('firstName')[0];
},

data: [
{firstName: 'Tommy', lastName: 'Maintz'},
{firstName: 'Ed', lastName: 'Spencer'},
{firstName: 'Jamie', lastName: 'Avins'},
{firstName: 'Aaron', lastName: 'Conran'}
]
})
};

var list;

if (!Ext.platform.isPhone) {
list = new Ext.List(Ext.apply(groupingBase, {
floating: true,
width: 350,
height: 350,
centered: true,
hideOnMaskTap: false
})).show();
}
else {
list = new Ext.List(Ext.apply(groupingBase, {
fullscreen: true
}));
}
new Ext.form.Toggle({
fieldLabel: 'Index Bar',
value: 1,
listeners: {
change: function(slider, thumb, old, val){
list.indexBar.setVisible(val != 0);
}
}
}).show();
new Ext.form.Toggle({
fieldLabel: 'Grouped',
value: 1,
listeners: {
change: function(slider, thumb, old, val){
list.setGrouped(val);
}
}
}).show();
}
});

shellgrit
18 Jul 2010, 11:38 PM
Thanks heaps Evan - it works beautifully!

Neil

jep
9 Jun 2011, 2:34 PM
This code doesn't really work anymore, so I've cranked out some that will. The standalone List extension code is in this thread (http://www.sencha.com/forum/showthread.php?136522-Here-s-a-list-that-can-be-grouped-ungrouped-and-fixes-some-other-grouping-bugs). Here's an example with the extension included:



<html>
<head>
<title>test</title>

<link rel="stylesheet" href="../sencha/resources/css/sencha-touch.css" type="text/css">
<script type="text/javascript" src="../sencha/sencha-touch-debug.js"></script>

<script type="text/javascript">

// jep @ Sencha Touch forums
Ext.namespace("Ext.jep");


Ext.jep.List = Ext.extend(Ext.List, {
// Use displayIndexToRecordIndex in the list's itemTap handlers to convert between
// the index in the handler (the display index) and the record index in the store.
// this isn't always the same when you have a custom grouping function that causes
// the store's to not sort identically to the display order (see bug report)
displayIndexToRecordIndex: function (targetIndex) {
if (this.grouped) {
var groups = this.getStore().getGroups();

for (var g = 0; g < groups.length; g++) {
var group = groups[g].children;

if (targetIndex < group.length)
return this.getStore().indexOf(group[targetIndex]);

targetIndex -= group.length;
}
}
else
return targetIndex;
},

// Use this to convert the store's record index to the display index
// for using the list's nodes directly via list.all.elements[i] or
// in calls to list.getNode.
recordIndexToDisplayIndex: function (targetIndex) {
if (this.grouped) {
var rec = this.getStore().getAt(targetIndex);

var groups = this.getStore().getGroups();
var currentIndex = 0;

for (var g = 0; g < groups.length; g++) {
var group = groups[g].children;

for (var i = 0; i < group.length; i++)
if (group[i] == rec)
return currentIndex;
else
currentIndex++;
}
}
else
return targetIndex;
},

// Fixes getNode so that if you pass it a store record, it will return the
// proper node even when the grouping/sorting situations described above happen.
getNode: function (nodeInfo) {
if (Ext.isString(nodeInfo)) {
return document.getElementById(nodeInfo);
} else if (Ext.isNumber(nodeInfo)) {
return this.all.elements[nodeInfo];
} else if (nodeInfo instanceof Ext.data.Model) {
var idx = this.recordIndexToDisplayIndex(this.store.indexOf(nodeInfo));
return this.all.elements[idx];
}
return nodeInfo;
},

// Fixes getRecord so that it gets the proper store record even when the
// grouping/sorting situations described above happen.
getRecord: function(node){
return this.store.getAt(this.displayIndexToRecordIndex(node.viewIndex));
},

// Fix for groupTpl not being able to accept an XTemplate
initComponent : function() {
Ext.jep.List.superclass.initComponent.apply(this);

if (this.grouped && this.groupTpl && this.groupTpl.html) {
this.tpl = new Ext.XTemplate(this.groupTpl.html, this.groupTpl.initialConfig);
}
},

// Allows for dynamically switching between grouped/non-grouped
setGrouped: function(grouped){
// we have to save the itemTpl user functions first, which are in different place depending on grouping
var memberFnsCombo =
(!this.grouped && this.tpl && this.tpl.initialConfig)
? this.tpl.initialConfig
: ((this.grouped && this.listItemTpl && this.listItemTpl.initialConfig) ? this.listItemTpl.initialConfig : {});

this.grouped = !!grouped;

// the following is code copied from List.initComponent, slightly modified
if (Ext.isArray(this.itemTpl)) {
this.itemTpl = this.itemTpl.join('');
} else if (this.itemTpl && this.itemTpl.html) {
Ext.apply(memberFnsCombo, this.itemTpl.initialConfig);
this.itemTpl = this.itemTpl.html;
}

if (!Ext.isDefined(this.itemTpl)) {
throw new Error("Ext.List: itemTpl is a required configuration.");
}
// this check is not enitrely fool proof, does not account for spaces or multiple classes
// if the check is done without "s then things like x-list-item-entity would throw exceptions that shouldn't have.
if (this.itemTpl && this.itemTpl.indexOf("\"x-list-item\"") !== -1) {
throw new Error("Ext.List: Using a CSS class of x-list-item within your own tpl will break Ext.Lists. Remove the x-list-item from the tpl/itemTpl");
}

this.tpl = '<tpl for="."><div class="x-list-item ' + this.itemCls + '"><div class="x-list-item-body">' + this.itemTpl + '</div>';
if (this.onItemDisclosure) {
this.tpl += '<div class="x-list-disclosure"></div>';
}
this.tpl += '</div></tpl>';
this.tpl = new Ext.XTemplate(this.tpl, memberFnsCombo);


if (this.grouped) {

this.listItemTpl = this.tpl;
if (Ext.isString(this.listItemTpl) || Ext.isArray(this.listItemTpl)) {
// memberFns will go away after removal of tpl configuration for itemTpl
// this copies memberFns by storing the original configuration.
this.listItemTpl = new Ext.XTemplate(this.listItemTpl, memberFnsCombo);
}
if (Ext.isString(this.groupTpl) || Ext.isArray(this.groupTpl)) {
this.tpl = new Ext.XTemplate(this.groupTpl);
}
// jep: this line added to original source
else if (this.grouped && this.groupTpl && this.groupTpl.html) {
this.tpl = new Ext.XTemplate(this.groupTpl.html, this.groupTpl.initialConfig);
}
}

// jep: slightly modified
if (this.rendered)
this.refresh();
}
});

Ext.reg('jeplist', Ext.jep.List);

Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady : function() {
Ext.regModel('Contact', {
fields: ['firstName', 'lastName']
});

var list = new Ext.jep.List({
groupTpl:new Ext.XTemplate(
'<tpl for=".">',
'<div class="x-list-group x-group-{id}">',
'<h3 class="x-list-header">{[this.formatGroup(values.group)]}</h3>',
'<div class="x-list-group-items">',
'{items}',
'</div>',
'</div>',
'</tpl>',
{ formatGroup:function (group) { return '<i>' + group + '</i>' } }
),

itemTpl:new Ext.XTemplate(
'<div class="contact">{[this.formatName(values.firstName, values.lastName)]}</div>', {
formatName:function (first, last) {
return last + ', ' + first;
}
}),
singleSelect: true,
grouped: true,

store: new Ext.data.Store({
model: 'Contact',
sorters: 'lastName',

getGroupString : function(record) {
return record.get('lastName')[0];
},

data: [
{firstName: 'Tommy', lastName: 'Maintz'},
{firstName: 'Steve', lastName: 'Apple'},
{firstName: 'Ed', lastName: 'Spencer'},
{firstName: 'Jamie', lastName: 'Avins'},
{firstName: 'Aaron', lastName: 'Conran'}
]
})
});

var mainPnl = new Ext.Panel({
layout:'fit',
fullscreen: true,
items:list,
dockedItems:{
xtype:'toolbar', dock:'top',
items:{ xtype:'button', text:'Toggle Group', handler:function () { list.setGrouped(!list.grouped) } }
}
});
}
});

</script>

</head>
<body></body>
</html>

ggvPVR
27 Sep 2011, 1:31 AM
Thanks for this. I've been using jeplist for my grouped list for a few weeks now.

Just made a change in my app to use the setGrouped feature. But when you turn off Grouped and scroll up you still see the group headers appearing and trying to stick to the top of the scroll area like when the groupby is on.

Your example here does it too. I'm getting same results on chrome desktop and iPhone.

jep
27 Sep 2011, 6:43 AM
Check the main thread for jeplist:

http://www.sencha.com/forum/showthread.php?136522-Here-s-a-list-that-can-be-grouped-ungrouped-and-fixes-some-other-grouping-bugs

(I fixed the original link above - sorry about that!)

In my testing, it doesn't do that with the current code there (the pinHeaders updates most likely fixed it). If you still run into problems, post something there to let me know and I'll check it out.

kingkoczon
12 Oct 2011, 7:32 PM
Hi Jep,

awesome code. is there any way to make the link go to a different page for each link when you click on them? like when I click on Steve Apple, it will slide right to a page on steve apple, and then you can click the back button to get back to the list. Is this possible? Thanks so much in advance. Again, awesome coding.

- BK

jep
12 Oct 2011, 7:46 PM
Thanks for the compliment!

Post some example code taking it as far as you can without the feature you're requesting, then detail a little what exactly you mean. I'll take a look at it later when I get a chance and see what I can do.

kingkoczon
12 Oct 2011, 9:40 PM
Thanks for the compliment!

Post some example code taking it as far as you can without the feature you're requesting, then detail a little what exactly you mean. I'll take a look at it later when I get a chance and see what I can do.

Hi Jep,

Thanks for the quick response.

Here is a link to a site with my coding on it. http://fixmysite.us/bobapp/indexnew.html.

I basically would like to be able to click (or tap) on a name, ie bob and have it go to a page with details about bob. Kind of like the iPhone contact app or ipod app, but the next page would be read-only.

Thanks again for checking it out. Really appreciate it.

jep
13 Oct 2011, 6:24 AM
Is this any different than just using the normal "disclosure" features of the list? Click on the "Disclosure" in the List example in the Kitchen Sink demo.