What events can I catch when the user leaves a GXT grid? - gwt

I have a web app that uses GXT (version 1.2.2) grids extensively. I'd like to warn the user if they make changes but don't save.
When I use the grid in a popup dialog, the only way for the user to leave is via a button (either Close or OK). If I add a SelectionListener to the Close button, I can do my "isDirty()" check and warn the user.
If I am not using a dialog, the restriction for leaving the page isn't there. The user can click on a side menu, select a different tab, hit a refresh or next page button that we have on each page. I could listen for an event on everyone of those, but is there an easier way? Something like a "before unload" event that gets fired?

Window.addCloseListener
Or in GWT 1.6:
Window.addCloseHandler
You cannot prevent the window from closing, but you can prompt the user to click cancel which will leave the page open. You can also perform a last-chance save operation after the user chooses to confirm the window close, but before your page is unloaded.

Try this:
Window.addListener(Events.Close,
new Listener<ComponentEvent>() {
public void handleEvent(ComponentEvent be) {
//Do something
}
});
or
Window.addListener(Events.Detach,
new Listener<ComponentEvent>() {
public void handleEvent(ComponentEvent be) {
//Do something
}
});

Related

GWT Forward Button

I'm having an issue handling the forward button.
Basically, when a user is on a page and has made changes without saving then presses the backwards or forwards button they are presented with a prompt and two options: Leave or Stay.
I have implemented the backwards button fine, and choosing to stay on the page works well using History.newItem(currentToken) - the back button is still clickable.
However with the forwards button, if I use History.newItem(currentToken), it brings this to the front of the history stack and the forward button can no longer be clicked.
History.replaceItem(currentToken) causes the same issue.
How do I handle the cancelling of a forwards action so that I stay on my current page, but the forwards button is still enabled?
#Override
public void onValueChange(ValueChangeEvent<String> event) {
logger.info("back button pressed: " + event.getValue());
String evenVal = event.getValue();
String token = History.getToken();
AbstractPresenter presenter = sessionKiosk.getCurrentlyShowingPresenter();
if (presenter instanceof NSRCommonWorksheetPresenter && sessionKiosk.isDirty()) {
((NSRCommonWorksheetPresenter)presenter).setHistoryToken(event.getValue());
((NSRCommonWorksheetPresenter)presenter).showUnsavedChangesLeavingPageDialog();
}
else {
handleHistoryEvent(event.getValue());
}
}
The dialog is shown and when I click on stay on page the following is called.
public void stayOnCurrentPage() {
if (eventMap.get(prevPage) != null) {
History.newItem(prevPage, false);
}
}
Update: Basically history.newItem(value) removes the use of the forward button. Is there another way to cancel the event? If I just do nothing, th page stays where i want but the url still updates
None of the 3 options in the else statement seem to work.
Thanks.
You can simply cancel the event without touching History or tokens.
UPDATE:
It appears from your code that you are not intercepting the event (back/forward button), but let it go through, get the new token, and then force a return to the previous state under certain circumstances.
I suggest using Activities and Places pattern where every "place" within your app has a corresponding "activity". Each activity in your app will implement GWT Activity interface which includes mayStop() method. This method is called before a user navigates away from a specific place in your app, giving you an opportunity to warn a user and cancel the navigation if necessary.
In general, this pattern offers a very robust support for the History mechanism, covering many use cases.
If you want to support History mechanism yourself, take a look at PlaceChangeRequestEvent - it allows you to warn a user who tries to navigate away from a place in your app.

webDialog "CURRENT GOALS" header but no button to authorize or cancel

I am new to Facebook SDK. I am adding the Facebook login in my Android app using Facebook SDK 3.0.1. A Facebook webDialog pop-up for login. Today I found a strange behaviour that is different from that yesterday.
After entering an account name and password and press Login button in the webDialog, it finally shows "CURRENT GOALS" in the header requesting add friends, enter locations, etc. The body shows "(YOUR_APP) would like to access your public profile and friend list". But there is no more button to authorize or cancel. From the Eclipse LogCat, the current session state from Session.StatusCallback() is OPENING. As a result, user cannot complete the login process.
So how do I pass the dialog to complete the login process successfully? If I press the upper-left corner close button or back button, the dialog closes. The session state would become CLOSED_LOGIN_FAILED.
Yesterday, when the same account login, the webDalog finally show "You have already authorized (YOURAPP)" and a Cancel and OK buttons exist. If user press OK, the dialog closes, and Session.StatusCallback() calls out session state becomes OPENED. Thus user complete the login process successfully.
I just found and posted a workaround at: current goals hides facebook ok button during Facebook authorization in a web view
In the FaceBookSDK I modified com/facebook/widget/WebDialog.java, so that once the web dialog was loaded it would look for the block that contains "Current Goals" and hide it (if it exists). Once you do that, the buttons are visible again (at least they were for me).
In com/facebook/widget/WebDialog.java:
private class DialogWebViewClient extends WebViewClient {
// ... other methods ...
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!isDetached) {
spinner.dismiss();
}
/*
* Once web view is fully loaded, set the contentFrameLayout background to be transparent
* and make visible the 'x' image.
*/
contentFrameLayout.setBackgroundColor(Color.TRANSPARENT);
webView.setVisibility(View.VISIBLE);
crossImageView.setVisibility(View.VISIBLE);
// I don't know how to highlight in the code block
// So I just add this extra long comment to make it obvious
// Add a javascript call to hide that element, if it exists
webView.loadUrl("javascript:try{ document.getElementById('nux-missions-bar').style.display='none'; } catch (e) {}");
// End changes
}

Why the OnBeforeUnload doesn't intercept the back button in my GWT app?

I have a hook on the beforeUnload event. If i try to click on a link, reload or close the tab or the navigator, the app ask for confirmation before leaving. That's the desired behavior.
But if click on the back button or the backspace outside an input, no confirmation.
At the beginning of the html :
window.onbeforeunload = function() {
if (confirmEnabled)
return "";
}
And i use the Gwt PlaceHistoryMapper.
What did i miss ? Where did i forgot to look ?
Thanks !
As long as you stay within your app, because it's a single-page app, it doesn't by definition unload, so there's no beforeunload event.
When using Places, the equivalent is the PlaceChangeRequestEvent dispatched by the PlaceController on the EventBus. This event is also dispatched in beforeunload BTW, and is the basis for the mayStop() handling in Activities.
Outside GWT, in the JS world, an equivalent would be hashchange and/or popstate, depending on your PlaceHistoryHandler.Historian implementation (the default one uses History.newItem(), so that would be hashchange).

How can I cancel the Window.ClosingEvent?

I want to show a modal window (instead the confirm) when a user close the browser. The GWT documention remarks that (during the handler operation) no user UI may be displayed during the shutdown. Exists any way cancel the operation, from the client not from the user?
Thanks in advance,
Oscar.
You can't cancel the close event. The best you can do is to let the user press cancel. I think the reason for this is to prevent "bad" javascript from not letting you close your browser.
This displays a dialog with a cancel option.
Window.addWindowCloseListener(new WindowCloseListener()
{
public String onWindowClosing()
{
return "You are about to exit from the application. Are you sure?";
}
public void onWindowClosed()
{
//cleanup code
}
});

Best Practices GWT Event Handling

I have a question regarding the event handling on client side in GWT.
In our application we have a quite complex structure of different modules and pages which are communicating via the gwt eventbus on client side. Now the amount of events is growing to fast for my opinion. E.g. I am opening a popup I need:
An event for opening the popup
An event for asking some data within the client
An event for getting back the data and fill in the dialog
An event for closing the popup
An event for handling the save Button
Am I thinking a little bit to complicated or missing something in the EventBus implementation? I just wanted to have some feedback out of the community as you are facing the same issues.
For what it's worth, I have lots of events and more growing. And yes, I wonder if I can do with less, but when I skip an event and link elements directly, I regret it.
Here's an example that I just fixed up yesterday. I have a DataGrid widget. I also support re-ordering of columns, hiding of columns, re-sizing columns, and coloring columns with a popup dialog. You click on a configure button, and a popup with the columns listed shows, and the user can click checkboxes to show or hide columns, click on a Move Up / Move Down button to re-order columns, and so on. Hit Apply on the popup and the popup disappears and the DataGrid re-configures.
Except that it didn't. You'd click on Apply and the popup would just sit there, the user would wonder what was going on, the DataGrid would re-configure underneath, and then the popup would go away. We're only talking a short amount of time -- maybe a second or a bit more -- but it was so so noticeable. Why was it happening? Because I got lazy and tied the popup directly to the configure button, and the Apply button directly to the DataGrid. You'd hit Apply, for example, and the call would be made to the DataGrid with the new configuration information. Only when the call returned would the popup would be torn down.
I knew it was bad when I did it, but I was being lazy. So I took the 20 minutes I needed to write up two messages and associated handlers in my mediator singleton. One message is issued by the DataGrid to start the configuration dialog, and one is issued by the popup to configure the DataGrid. Now the widgets are de-coupled, and the performance is much snappier. There is no sense of "stickiness".
Now to your example, can you not combine (1) and (2)? And also (3), (4), and (5)? When the user clicks the configure button on my app, the event carries with it the current configuration information (including a reference to the DataGrid that originated the request). You can call this information the "payload". When the user clicks the Apply button on the popup, the event payload includes all the new configuration information (including a reference to that original target DataGrid) that the event handler feeds to the target DataGrid when the event is handled. Two events -- one to kick off the configuration and one to apply the end result.
Yes there are a plethora of events in any app that does something interesting, but events can carry a lot of information, so I would look at whether your event organization is too fractured.
As a extra bit, here is the code I use. I shamelessly copied elements of this pattern from one of Google's examples.
The user can ask for help using a menu item:
#UiField
MenuItem help;
help.setCommand(new Command() {
#Override
public void execute() {
BagOfState.getInstance().getCommonEventBus().fireEvent(new MenuHelpEvent());
}
});
For the event (in this case, the event fired when the user clicks on the Help menu item):
public class MenuHelpEvent extends GwtEvent<MenuHelpEvent.Handler> {
private static final Type<Handler> TYPE = new Type<Handler>();
public interface Handler extends EventHandler {
void doMenuHelp();
}
#Override
public GwtEvent.Type<Handler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(Handler handler) {
handler.doMenuHelp();
}
public static HandlerRegistration register(EventBus eventBus, Handler handler) {
return eventBus.addHandler(TYPE, handler);
}
}
I have a singleton called Mediator in which ALL events are registered:
MenuHelpEvent.register(BagOfState.getInstance().getCommonEventBus(),
new MenuHelpEvent.Handler() {
#Override
public void doMenuHelp() {
new MenuHelp().execute();
}
});
Every event is mated with a Command object to do the work:
public class MenuHelp implements Command {
#Override
public void execute() {
new InfoMessage(BagOfState.APP_MSG.unimplementedFeatureCaption())
.setTextAndCenter(BagOfState.APP_MSG.unimplementedFeature());
}
}
Everything is decoupled. The menu widget is bound to a command that executes and then completes. The command fires the event on the bus then completes. The event fires off the execution of a Command and the completes. The Command shows the popup help panel (in this case, an "unimplemented" message to the user -- yeah, I'll get to it soon). Every interaction with a user's input is handled extremely quickly and resolves. It can kick off a series of events to perform a long action, but never tying up the GUI to do so. And of course, since the elements are decoupled, I can call the same elements in other places (for instance, call the help Command through a button push as well as a menu item).