PDA

View Full Version : TabPanel event manipulation / synchronous messagebox (confirm)



firegun
30 Apr 2010, 1:28 PM
Hello,

Well I didnīt finded a way to solve my problem, or another place talking about this, but I think itīs a commom wish. Im my application there are windows that contains a tabPanel with a grid in one tab and a form on other, a pretty trivial list/edit module, what I want to do is, if the form tab is selected, and the form itself isDirty, open a messagebox asking the user if he wantīs to lose the changes he made.

Iīm facing some problems with this simple idea, Iīve tried 2 approachs:

1- Using JS confirm on beforetabchange event (of the tabPanel): This works as a clock. The execution is stoped till the user take some desicion, then the tab change can be stoped or not, based on the user desire. But js confirm is ugly and... well we want to keep on a pure ExtJS design ;)

2- Using a Ext.MessageBox.confirm on beforetabchange event (of the tabPanel): The ExtJS confirm is not as the normal JS one. So, Ext will show the message box and keep running (the tab get changed on the background as the box is showed), the user decision trigger a callback function. I donīt wanna the tab to get changed, then I change it back. To stop the tab changing I can return false, but after the user decision, I could want the tab to change, and if I want so, I may get trapped on the beforetabchange again... well I didnīt find a way to solve this...

Some ideas? Iīm not posting code here now because I have lot of stuff I would need to strip from my tests to show the problem only, if some1 interested on help me thinks that is should help, Iīll take the time to show my point :)

cnelissen
30 Apr 2010, 2:12 PM
This seems like a pretty easy problem to solve, though I did not test the example below. Basically the logic would return false in the beforetabchange event if the form is dirty, then prompt the user, if the user wants to lose changes, then change the tab... Something like this should work:



var tabPanel = new Ext.TabPanel({
listeners: {
'beforeTabChange': function(panel, newTab, currentTab) {
if (form.isDirty()) {
confirmTabChange(newTab);
return false;
}
}
}
});

function confirmTabChange(newTab) {
Ext.MessageBox.confirm('Alert', 'Are you sure you want to change tabs?', function(response) {
if (response == 'yes') {
tabPanel.setActiveTab(newTab);
}
});
}

firegun
1 May 2010, 12:07 PM
Hey! Tnks a lot for you time and attention. But this solution is not viable, at least not only like this, cause as I had commented on my post after we return false to the event, the tab change is stooped, the message is showed to the user, and if he says that he want to lost the not saved data, when you run tabPanel.setActiveTab(newTab), the event beforetabchange is fired again, and we enters on a infinity loop. To show this Iīve compiled a simple example using what you sad (that was actually my second approach), and what I sad:



var formPanel = new Ext.form.FormPanel({
title: 'Form',
frame: true,
border: false,
items: [{
xtype: 'textfield',
name: 'name',
fieldLabel: 'Name',
allowBlank: false
},{
xtype: 'textfield',
name: 'email',
fieldLabel: 'E-mail'
}]
});

var tabPanel = new Ext.TabPanel({
renderTo: document.body,
activeTab: 0,
width:600,
height:250,

defaults:{autoScroll: true},

items: [
{
title: 'Grid',
html: 'Grid here.'
},
formPanel
],

listeners: {
'beforetabchange': function (tabPanel, newTab, currentTab) {
if (formPanel.form.isDirty()) {
//Option 1 - Using JS confirm
return confirmTabChange();

//Option 2 - Using ExtJS MessageBox
//confirmTabChangeExt(newTab);
//return false;
}
}
}
});

function confirmTabChange() {
if (confirm('You about to lose all no saved data. Do you want to continue ?')) {
formPanel.form.reset();
return true;
} else {
return false;
}
}

function confirmTabChangeExt(newTab) {
Ext.MessageBox.confirm('Alert', 'Are you sure you want to change tabs?', function(response) {
if (response == 'yes') {
tabPanel.setActiveTab(newTab);
}
});
}
The code above should work as I want to, but using the JS confirm function, comment the option 1 and uncomment the option 2 then you will see what Iīm talking about :) Again tnks for you time.

evant
1 May 2010, 4:18 PM
Did you read the MessageBox docs?



Note that the MessageBox is asynchronous. Unlike a regular JavaScript alert (which will halt browser execution), showing a MessageBox will not cause the code to stop. For this reason, if you have code that should only run after some user feedback from the MessageBox, you must use a callback function (see the function parameter for show for more details).

firegun
1 May 2010, 5:27 PM
Yeah I read that, I know that the MessageBox is not supposed to be synchronous, I never sad that the MessageBox donīt work or wathever, the question here is how I may get the same effect that I have with the simple JS confirm using ExtJS MessageBox, or how I can change the tab after the user interaction not being caught on the beforetabchange event again... well how I suppose to get this simple behavior, check if the from has changed and ask the user to save or lose the changes before he leaves the tab where the form is at.

rstuart
1 May 2010, 6:39 PM
This solution might be a little dirty, but it should work. It is a slight variation on Clint's solution.

You should be able to accomplish what you want with the use of the 'single' option when adding the listener. The single option only executes your listener once. Using this, when the user clicks no, you can add the listener again.

Below is a version of Clint's code using the single option and should work for you:


var tabPanel = new Ext.TabPanel({
listeners: {
'beforeTabChange':
fn: function() {
if (form.isDirty()) {
confirmTabChange(newTab);
return false;
}
single: true
}
}
});

function confirmTabChange(newTab) {
Ext.MessageBox.confirm('Alert', 'Are you sure you want to change tabs?', function(response) {
if (response == 'yes') {
tabPanel.setActiveTab(newTab);
} else {
tabPanel.on({
'beforeTabChange': {
fn: function() {
if (form.isDirty()) {
confirmTabChange(newTab);
return false;
}
}
single: true
}
})
}
});
}

You are going to have a problem is the user clicks back into the tab obviously because you have cleared the listener. The solution to this is to add the listener when the tab is activated.

Hope this helps.

firegun
1 May 2010, 9:34 PM
Well, I wanna tank everyone that has contributed to this thread! Specialy rstuart that called my attention to the single option on the listeners. That made the trick, I had to work a little farther to get a consistent behavior. Iīll explain my solution to the case. To this work, we have to be sure that as soon as the form values change I attach the listener on the tab change, and if the values became same as original, the listener is removed. To archive that Iīve tried to bubble the change event from fields on the FormPanel, but the change event happens just before the blur, if the field is changed and you just click in other tab the field donīt lose the focus, and the change event donīt get fired. Then I moved to the clientvalidation event of the form, adding the monitorValid: true on the form configuration.

Letīs stop the talking and show the working code!



var formPanel = new Ext.form.FormPanel({
title: 'Form',
frame: true,
border: false,

monitorValid: true, //This makes the form fire the clientvalidation event

items: [{
xtype: 'textfield',
name: 'name',
fieldLabel: 'Name',
allowBlank: false
},{
xtype: 'textfield',
name: 'email',
fieldLabel: 'E-mail'
}],

/**
* Commom form buttons :)
*/
buttons: [
{
text:'Save',
handler: function () { formPanel.form.submit(); },
formBind: true
},{
text:'Reset',
handler: function () {
formPanel.form.reset();
formPanel.checkTabChangeListener();
}
},{
text:'Cancel',
handler: function () {
formPanel.form.reset();
formPanel.checkTabChangeListener();
tabPanel.setActiveTab(0);
}
}
],

listeners: {
'clientvalidation' : {
fn: function () {
this.checkTabChangeListener();
}, scope: formPanel
}
},

/**
* This function check is the form isDirty and if the tabPanel has
* the listener attached, and take some action
*/
checkTabChangeListener: function () {
if (this.form.isDirty() && !tabPanel.hasListener('beforetabchange')) {
console.info('On beforetabchange');
this.setTabChangeListener();
} else if (!this.form.isDirty() && tabPanel.hasListener('beforetabchange')) {
console.info('Un beforetabchange');
tabPanel.un('beforetabchange', this.onBeforeTabChange);
}
},

/**
* This function is the actual beforetabchange listener
*/
onBeforeTabChange: function (tabPanel, newTab, currentTab) {
Ext.MessageBox.confirm(
'Alert',
'There are not saved data. Do you wanna lose it?',
function(response) {
if (response == 'yes') {
this.form.reset();
tabPanel.setActiveTab(newTab);
} else {
this.setTabChangeListener();
}
},
formPanel
);
return false;
},

/**
* This function attach the beforetabchange listener on the tabPanel,
* and is separeted cause itīs used on the clientvalidation event check
* and if the user say that he donīt wanna lose the changes
*/
setTabChangeListener: function () {
tabPanel.on({
'beforetabchange' : {
fn: this.onBeforeTabChange,
single: true
}
});
}
});

var tabPanel = new Ext.TabPanel({
renderTo: document.body,
activeTab: 0,
width:600,
height:250,

defaults:{autoScroll: true},

items: [
{
title: 'Grid',
html: 'Grid here.'
},
formPanel
]
});
I hope this can help some1 else, I still think that itīs a too sophisticated solution for a so trivial problem that could be solved easily with a JS confirm as I showed before, maybe could be another way to solve it, some1?

16 May 2010, 3:06 AM
Hi try this instead,

it works for me and takes lesser ln to implement:
basically u just need to suspend n resume the events to prevent infinite looping >>>

TabPanel.on('beforetabchange', function(tabPanel, newTab, currentTab) {
if (currentTab != null) { //do not prompt msg on initComponent
Ext.MessageBox.confirm('Alert', 'Changing tab will remove data in current tab. Do you to continue?',
function(response) {
if (response == 'yes') {
tabPanel.suspendEvents();
//reset current tab values before activating new tab...
alert('reset current tab values before activating new tab...currTab = '+currentTab.id);
if (currentTab.id == 'tab1') { //reset tab1
} else { //reset tab2
}
tabPanel.setActiveTab(newTab);
//alert(newTab.id);
tabPanel.resumeEvents();
} else {
currentTab.doLayout();
}
},this);
return false;
}
return true;
});