Detecting changes in a GWT TextBox by Sinking Events

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());
  }
});


This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to Detecting changes in a GWT TextBox by Sinking Events

  1. Pingback: Modifing the GWT Source for New Events | GWT Grab Bag

  2. Pingback: GWT sinkEvent | Life in USA

Leave a comment