I had a requirement where I needed to detect a change in a TextBox as data is entered in a form. The first thing I tried was using the KeyUpEvent. It seemed reasonable enough and worked great.
However I soon tried to copy and paste into one of the TextBoxes on the form and it became apparent that I had a problem. While I was looking for KeyUpEvents and that worked OK, a paste to the TextBox was not detected.
So next I tried using ValueChangeEvent as well as the KeyUpEvent. The ValueChangeEvent did work but not entirely as I needed. The problem is that the ValueChangeEvent is not fired until the TextBox that data is entered into loses focus.
I starting looking for alternatives and came across the onPasteEvent. This performed exactly as I needed, the event would fire immediately as text is pasted into the TextBox.
The onPasteEvent can be detected by GWT but there is not a handler for it. To use the onPasteEvent I would need to create a custom Widget to sink the event and then fire a custom event to signal that the onPasteEvent was caught. After creating my custom event and extending the TextBox widget everything was working OK.
Or so I thought. Unfortunately I had not achieved complete success yet. The onPasteEvent works for all the browsers I tested except for Opera. Opera does not support the onPasteEvent.
Opera has an oninput JavaScript event that can be used to detect changes much like the onPasteEvent but GWT does not support this event. In order to act on this event in GWT I modified the GWT source to listen to the oninput events. See my previous blog entry here (https://gwtgrabbag.wordpress.com/2010/12/26/modifing-the-gwt-source-for-new-events/) where I describe how I did this.
There are two things I need to do, first create a new custom event. Second extend the TextBox widget to sink the events I want to catch and then fire the new custom event.
.
Creating a Custom Event
I found a great reference for creating custom events here: http://stackoverflow.com/questions/2951621/gwt-custom-events
Step 1: Define New Event
The first thing is to define a new event. Since the goal is to fire an event when the text changes I decided to call this event TextChangeEvent. Here is the source code:
public class TextChangeEvent extends GwtEvent<TextChangeEventHandler> { public static final Type<TextChangeEventHandler> TYPE = new Type<TextChangeEventHandler>(); @Override public Type<TextChangeEventHandler> getAssociatedType() { return TYPE; } @Override protected void dispatch(TextChangeEventHandler handler) { handler.onTextChange(this); } }
Step 2: Define the Event Handler Interface
Next create an interface for the receiver of this event type. Here is the source code:
public interface TextChangeEventHandler extends EventHandler { void onTextChange(TextChangeEvent event); }
.
Creating a Custom TextBox Widget
Now create the custom widget that can sink the events to listen for and then fire the new event. I am extending the TextBox widget. This example relies on the code changes I made to GWT to listen to oninput events. If you do not want to modify GWT source code then see this other example for using JSNI for the oninput events (https://gwtgrabbag.wordpress.com/2010/12/26/gwt-detecting-changes-in-a-textbox-with-event-filtering-and-jsni)
Here is the source code for the extended TextBox:
public class ExtendedTextBox extends TextBox implements HasHandlers { private HandlerManager handlerManager; public ExtendedTextBox() { super(); handlerManager = new HandlerManager(this); // For all browsers - catch onKeyUp sinkEvents(Event.ONKEYUP); // For IE and Firefox - catch onPaste sinkEvents(Event.ONPASTE); // For Opera - catch onInput sinkEvents(Event.ONINPUT); } @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); switch (event.getTypeInt()) { case Event.ONKEYUP: case Event.ONPASTE: case Event.ONINPUT: { // Scheduler needed so pasted data shows up in TextBox before we fire event Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { fireEvent(new TextChangeEvent()); } }); break; } default: // Do nothing } } @Override public void fireEvent(GwtEvent<?> event) { handlerManager.fireEvent(event); } public HandlerRegistration addTextChangeEventHandler(TextChangeEventHandler handler) { return handlerManager.addHandler(TextChangeEvent.TYPE, handler); } }
Testing it in a Form
Option 1 – Use UIBinder
Here is an example of using the widget in a form with UIBinder:
@UiField ExtendedTextBox nameTextBox; ... @UiHandler("nameTextBox") public void onNameTextChange(TextChangeEvent textChangeEvent) { Window.alert("Name TextBox has Changed to: " + nameTextBox.getValue()); }
Option 2 – Non UIBinder Example
Here is an example of using the widget without UIBinder:
ExtendedTextBox nameTextBox = new ExtendedTextBox(); nameTextBox.addTextChangeEventHandler(new TextChangeEventHandler() { @Override public void onTextChange(TextChangeEvent event) { Window.alert("Name Value Changed to: " + nameTextBox.getValue()); } });
Pingback: Modifing the GWT Source for New Events | GWT Grab Bag
Pingback: GWT sinkEvent | Life in USA