问题
Say I have a button that triggers a push of a new view. I noticed that if I click it more than once, fast enough, it will push the same view twice.
You can mimic this behavior using their official docs on this page, where they have a live sample: http://docs.sencha.com/touch/2-0/#!/guide/navigation_view
the clear question is, simply how to prevent it?
回答1:
Masking successfully prevents double tapping problem.
In my code I'm using two functions for mask/unmask navigation container:
/**
* Mask container with rolling wheel. Usually need if Ajax-request is sent to the server and app waiting for response
* Best practice is masking the current navigator container, to prevent blocking whole app. Method warns if no container
* is defined. In some cases warning could be suppress with parameter
*
* @param container
* @param {boolean} [suppressWarning]
*/
startLoading: function(container, suppressWarning) {
var loadingComponent = container;
if (!loadingComponent) {
// <debug>
if (!suppressWarning) {
console.warn('Please define navigator container for non-blocking operation, or define suppressWarning parameter');
}
// </debug>
loadingComponent = Ext.Viewport;
}
// var lastMaskedContainer = container;
this.lastMaskedContainer = container;
loadingComponent.setMasked({
xtype: 'loadmask',
message: 'Loading...'
});
/*
Ext.defer(function() {
lastMaskedContainer.setMasked(false);
}, Pipedrive.app.maskingTimeout * 1000)
*/
},
/**
*
* @param {Ext.Container} container
* @param {boolean} [suppressWarning]
*/
stopLoading: function(container, suppressWarning) {
var loadingComponent = container;
if (!loadingComponent) {
// <debug>
if (!suppressWarning) {
console.warn('Please define either navigator container for non-blocking operation, or define suppressWarning parameter');
}
// </debug>
loadingComponent = Ext.Viewport;
}
var alreadyMasked = loadingComponent.getMasked();
var lastMaskedContainer = this.lastMaskedContainer;
if (!alreadyMasked && !suppressWarning) {
// <debug>
if (lastMaskedContainer != container) {
console.warn('Found Start/Stop Loading inconsistency. Please revise code'
+ (container ? '. Container: ' + container.getId() : 'Ext.Viewport')
+ (lastMaskedContainer ? ', last masked container: ' + lastMaskedContainer.getId() : '')
);
}
// </debug>
loadingComponent = Ext.Viewport;
}
loadingComponent.setMasked(false);
}
than in the tap handler:
onDealDetailsTap: function(ct) {
console.log('onDealDetailsTap', ct);
var form = ct.getReferenceForm(),
navigatorContainer = this.getNavigatorContainer(form),
model = form.getRecord();
UiHelper.startLoading(navigatorContainer);
Ext.Viewport.fireEvent('detailfields', {
title: model.get('title'),
id: model.get('id'),
store: 'DealFields',
navigatorContainer: navigatorContainer
})
},
to cleanup the loading mask:
control : {
activitiesContainer: {
push: 'onPushActivitiesContainer'
},
onPushActivitiesContainer: function(ct) {
//console.log('onPushActivitiesContainer', ct);
UiHelper.stopLoading(ct);
},
especially it is cool for waiting for long-timed ajax requests....
Cheers, Oleg
回答2:
Another method is to check what the active view is, and only push if it is not the same as the view you are about to push. I've tested this and it works.
E.g.
if (this.getNavigationView().getActiveItem().xtype != "someView") {
this.getNavigationView().push({ xtype: "someView" });
}
回答3:
Extending jayteejee's answer, I've overridden the push method in a custom navigation view, like this:
Ext.define('BT.navigation.View', {
extend: 'Ext.navigation.View',
xtype: 'btnavigationview',
push: function (view) {
if(this.getActiveItem().xtype != view.xtype)
this.callParent(arguments);
else
console.warn("Prevented pushing a potentially duplicate view of xtype: " + view.xtype);
}
});
I'm not totally sure if the xtype
assumption is safe enough, but I can't think of any situation in my current app that would require one view pushing another view of the same type onto the navigation stack. So, the solution works for me, and it's pretty neat. The warning is there to save me headache later on and possibly pulling my hair out trying to work out why push
wouldn't work!
回答4:
Just suspend the events on the button when it's tapped and resume them when the view is pushed
button.suspendEvents();
...
button.resumeEvents();
I don't think there is another way. As a developer or a user, when you tap a button twice, you expect the event handler to be called twice.
Hope this helps
回答5:
simply mask the entire container and then unmask it; create a ref for the container or panel in which the button exists in your controller and on tap set:
ref.setMasked(true)
After the new view is pushed simply unmask by
ref.setMasked(false)
回答6:
Another way is to flip a parameter once the list item has been tapped once, like this:
{
onListItemTap: function () {
if (!this.tapped) {
this.tapped = true;
...
}
}
}
Of course, that only works if you are destroying the list view as soon as the user goes to a different screen.
回答7:
I created a method for checking this:
ENSURE_NO_DOUBLE_TAP : function(classNameToPush) {
if (Ext.getClassName(Ext.getCmp('MyViewport').getActiveItem()) == classNameToPush) {
return false;
}
return true;
}
Then from your app before anything that could be double tapped is processed:
if (!ENSURE_NO_DOUBLE_TAP('MyApp.view.View')) {
return;
}
回答8:
If you are listening to the tap event of a button using listeners,then here is my solution:
listeners : {
release : function(){
if(this.getDisabled())return false;
this.setDisabled(true);
this.fireEvent('tap');
},
tap : function() {
//do what you want
}
}
回答9:
Extending on jayteejee's and Merott's answers, I've added some code to intercept on multiple fast pushes to not only prevent duplicates but to prevent pushing of different views as well before the page transition completes. Think of a user tapping different list items.
Also notice the view.destroy();
method in the else block to prevent view instances from heaping up in memory.
Ext.define('Overrides.navigation.View', {
extend: 'Ext.navigation.View',
xtype: 'ovrnavigationview',
interceptPush: false,
push: function (view) {
var activeItem = this.getActiveItem();
// Prevent multiple pushes & duplicates
if (!this.interceptPush && activeItem.xtype !== view.xtype) {
// Set interceptPush
this.interceptPush = true;
// Reset interceptPush after 500 ms
Ext.defer(function() {
this.interceptPush = false;
}, 500, this);
// Handle push
this.callParent(arguments);
} else {
// Warn developer
console.warn("Prevented pushing view of xtype: " + view.xtype);
// Destroy view
view.destroy();
return false;
}
}
});
回答10:
You can just use the "itemsingletap" event.
If you want to support double taps as well, make a second listener for "itemdoubletap" and invoke the same function, both listeners work fine together.
来源:https://stackoverflow.com/questions/11522736/sencha-touch-clicking-a-button-rapidly-will-push-a-view-twice