PDA

View Full Version : button in itemTpl impossible to handle?



vivendi
12 Sep 2011, 6:31 AM
Hi,

I'm creating a list and also using the itemTpl to add some custom html.
I also added an <input type="button" /> to the itemTpl. So whenever i see my list i also see a button at the right of the item list.

But whenever i press this button then the event of the items list (itemtab) is fired too. Is there a way to avoid this??

So that when i select an item somewhere in my list that the event 'itemtab' gets fired (like it should), but NOT when i press on the button inside of an item. That i only want to use the event of the button. Right now the itemtab gets fired aswell eventhough i clicked on the button and not really on the item itself.

Anyone any idea how to do that??

NickT
12 Sep 2011, 9:52 AM
what if you return false in the onclick event handler for the button by adding that event handler to your input html element


onclick="return false;"

vivendi
12 Sep 2011, 10:07 AM
what if you return false in the onclick event handler for the button by adding that event handler to your input html element


onclick="return false;"


Sorry, i'm not sure what you mean...?

Just to make sure i'll try to clarify my problem.

I have a list with 4 items. In each item i have an <input type="button" onclick="alert('aaa');" /> element.

I can select an item from the list, after that i use the "itemtab" event to see what item got selected in the list.

I can also click on one of the buttons and do something (by using onclick() for example). The only problem is, whenever i click on a button, the item from the list also gets selected.
When i click on the button again, the item gets deselected.

But i don't want that to happen because i'm not really clicking on a list-item, i'm clicking on a button INSIDE the list-item...

And besides, because of this its not only triggering the onclick() event from the button, but also the click event of the list-item everytime i click on the button! I dont want that.\

When i click on the button, the only event that should be triggered is the onclick() event.

But it seems that this isn't possible...??

NickT
12 Sep 2011, 11:17 AM
What I would suggest you consider is extending List and add in buttons similar to the onItemDisclosure feature. With the item disclosure, you can have a button dropped in along the right edge of the list item and trap that event. You may wish to have your own custom button, or even an array of buttons. I am including an extension that I did of list to add an array of 2 extra buttons in addition to the itemDisclosure button, giving me a set of 3 buttons that i can trap independently of the item click itself.


28021
Example extension: so, what i have added is an analyze button and a statuschange button that raises those events....

Ext.namespace('Ext.ux');

/**
* @class Ext.form.SpecialsList
* @extends Ext.List
* <p>Specialized list for specials that includes analytics link as well as status change link {@link Ext.SpecialsList}.</p>
* @xtype datepickerfield
*/
Ext.ux.SpecialsList = Ext.extend(Ext.List, {
preventSelectionOnStatusChange: true,
preventSelectionOnAnalysis: true,
// @private
initComponent : function() {






var memberFnsCombo = {};


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>';
this.tpl += '<div class="x-list-status"></div>';
this.tpl += '<div class="x-list-analysis"></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);
}
}
else {
this.indexBar = false;
}


if (this.scroll !== false) {
this.scroll = {
direction: 'vertical',
useIndicators: !this.indexBar
};
}


Ext.List.superclass.initComponent.call(this);


if (this.onItemDisclosure) {
// disclosure can be a function that will be called when
// you tap the disclosure button
if (Ext.isFunction(this.onItemDisclosure)) {
this.onItemDisclosure = {
scope: this,
handler: this.onItemDisclosure
};
}
}


this.on('deactivate', this.onDeactivate, this);


this.addEvents(
/**
* @event disclose
* Fires when the user taps the disclosure icon on an item
* @param {Ext.data.Record} record The record associated with the item
* @param {Ext.Element} node The wrapping element of this node
* @param {Number} index The index of this list item
* @param {Ext.util.Event} e The tap event that caused this disclose to fire
*/
'disclose',


/**
* @event update
* Fires whenever the contents of the List is updated.
* @param {Ext.List} list This list
*/
'update',


'analyze',


'statuschange'
);
},
afterRender : function() {
Ext.ux.SpecialsList.superclass.afterRender.call(this);


this.mon(this.getTargetEl(), 'singletap', this.handleStatusChange, this, {delegate: '.x-list-status'});
this.mon(this.getTargetEl(), 'singletap', this.handleAnalysis, this, {delegate: '.x-list-analysis'});
if (this.onItemDisclosure) {
this.mon(this.getTargetEl(), 'doubletap', this.handleItemDisclosure, this);
}
},
//@private
handleStatusChange : function(e, t) {
var node = this.findItemByChild(t),
record, index;


if (node) {
record = this.getRecord(node);
index = this.indexOf(node);
if (this.preventSelectionOnStatusChange) {
e.stopEvent();
}
this.fireEvent('statuschange', record, node, index, e);
}
},
//@private
handleAnalysis : function(e, t) {
var node = this.findItemByChild(t),
record, index;


if (node) {
record = this.getRecord(node);
index = this.indexOf(node);
if (this.preventSelectionOnAnalysis) {
e.stopEvent();
}
this.fireEvent('analyze', record, node, index, e);
}
}
});
// register xtype
Ext.reg('specialslist', Ext.ux.SpecialsList);




So, here is an example of using this extension inline... Notice my itemTpl has no buttons in it, Those are created by the control extension and the events that are raised are done from that extension as well.


{ cls: 'ts-list',
xtype: 'specialslist',
width: '100%',
scroll: 'vertical',
itemTpl: '<table class="ts-list-specials-row-text">' +
'<tr>' +
'<td rowspan="5">' +
'<img class="ts-list-specials-row-image" src="{image}" />' +
'</td>' +
'<td class="ts-list-specials-row-label">' +
'{title}' +
'</td>' +
'</tr>' +
'<tr>' +
'<td><p>' +
'{venueName}' +
'</p></td>' +
'</tr>' +
'<tr>' +
'<td><p>' +
'{subtitle}' +
'</p></td>' +
'</tr>' +
'<tr>' +
'<td><p>' +
'{detail}' +
'</p></td>' +
'</tr>' +
'<tr valign="bottom">' +
'<td>' +
'{startTime} - {endTime}' +
'</td>' +
'</tr>' +
'<tr>' +
'<td class="ts-list-specials-status-{status}" rowspan="2"><p>' +
'{status}' +
'</p></td>' +
'<td>' +
'{startDate} - {endDate}' +
'</td>' +
'</tr>' +
'</table>',
cls: 'ts-list',
onItemDisclosure: function(record, btn, index) {
vm.activateItem('Detail');
Ext.getCmp('Detail').load(record);
// this would be uncommented in lieu of the form.load call if we activate the bindContext logic
// select the record in the store, and load the detail view
{modelPropName: field.name});
},
// bind to places store
store: '${specials}',
listeners: {
'update': function(list) {
list.setLoading(false);
},
'analyze': function(record, node, index, evt) {
console.log('analyze button clicked');
},
'statuschange': function(record, node, index, evt) {
console.log('statuschange clicked');
}
}
}
]
}

vivendi
12 Sep 2011, 12:42 PM
Excellent! Thanks alot for sharing that.

jep
12 Sep 2011, 1:19 PM
To give you another approach, here's how I handle it:

Create a button with a css class in your template:



<button class="detailsButton">Details</button>


Then in the list's itemtap event, do this:



function listItemTapped(list, index, item, evt) {
if (evt.getTarget('.detailsButton'))
handleDetailsButtonClicked(item);
}


That way you know the button was clicked and on which item without having to do some coding gymnastics to find out.

vivendi
12 Sep 2011, 10:58 PM
To give you another approach, here's how I handle it:

Create a button with a css class in your template:



<button class="detailsButton">Details</button>


Then in the list's itemtap event, do this:



function listItemTapped(list, index, item, evt) {
if (evt.getTarget('.detailsButton'))
handleDetailsButtonClicked(item);
}


That way you know the button was clicked and on which item without having to do some coding gymnastics to find out.

I tried your solution. It would indeed be better if this could be done with much less code. But unfortunately i couldn't get it to work.

The list-item still gets selected after i clicked on the button.


listeners: {
itemtap: function ( list, index, item, e ) {
if ( e.getTarget('.mybutton') )
{
alert('aaa');
//return;
}
else
{
alert('bbb');
//return;
}
}
}


I do get to see the alert. I also tried to return false. But that didn't fo the trick either.

Any idea what i'm doing wrong?

jep
13 Sep 2011, 7:38 AM
Yeah, a bit of a pickle if you don't want the selection to happen. What you'd probably need to do is set the list's selectedItemCls (and probably pressedCls, if you didn't want that happening) to some a non-existent CSS class. Then in the itemTap, you'd need to manually set/unset x-item-selected if it wasn't the button being clicked on. Does this example make sense?



Ext.regModel('example', {
fields: ['title']
});

Ext.setup({
onReady: function() {
var lastItem;

var list = new Ext.List({
fullscreen:true,
itemTpl: '<div>{title}<button class="detailsButton">Button</button></div>',
selectedItemCls:'noselect',
pressedCls:'nopress',
store:{
xtype:'jsonstore',
model:'example',
data:[
{title:'Item 1'},
{title:'Item 2'},
{title:'Item 3'}
]
},
listeners:{
itemtap:function(list, index, item, e) {
if (e.getTarget('.detailsButton')) {
alert('button clicked: ' + list.store.getAt(index).data.title);
}
else {
if (lastItem) {
lastItem.setAttribute('class', 'x-list-item');
}

lastItem = item;

item.setAttribute('class', 'x-list-item x-item-selected');
}
}
}
});
}
});


I didn't do anything with x-item-pressed other than turn it off. So you'll have to add some more code to make that happen. You might even need to override onTapStart/onTapEnd/onTapCancel to get in the right place.

edspencer
13 Sep 2011, 6:43 PM
This has been asked for a great deal, very happy to say that it's baked into the framework in Sencha Touch 2 :)

jep
13 Sep 2011, 9:27 PM
Great to hear!

Care to give any details beyond that? Is it simply for buttons, or can you easily embed various objects like Lists, Buttons, etc.?

edspencer
13 Sep 2011, 9:41 PM
Great to hear!

Care to give any details beyond that? Is it simply for buttons, or can you easily embed various objects like Lists, Buttons, etc.?

Anything and everything :) I think you'll be very happy with the flexibility we're bringing here

Saaisindhu
12 Dec 2012, 11:36 PM
Can you please zip this project and post it