GWT ramblings of a Flex developer – live search component

Recently, I had to create a custom component that does live filtering on data presented in a grid.

If you are reading this series, you probably know how this can be done in Flex. If you don’t, a quick google search will probably point you in the right direction. Enough to say that in envolves a custom component to get the search criteria and a filter function on the grid’s dataProvider ArrayCollection.

I was surprised this is relatively straight forward in GWT (using uiBinder and GXT 3.0)

Code snippets are attached inline, I can’t attach complete code to protect customer code.
First, we’ll create the SearchBar.ui.xml:

	<c:HBoxLayoutContainer>
		<f:FieldLabel text="Search">
			<f:widget>
				<f:TextField ui:field="searchCriteria" allowBlank="true" emptyText="Enter search criteria..."/>
			</f:widget>
		</f:FieldLabel>
	</c:HBoxLayoutContainer>

Note, that the TextField is defined as ‘ui:field’ to allow access to it from the view class.

Next, we create the SearchBar.java view class:

public class SearchBar extends Composite {

	private static SearchBarUiBinder uiBinder = GWT.create(SearchBarUiBinder.class);

	@UiField
	TextField searchCriteria;

	interface SearchBarUiBinder extends UiBinder<Widget, SearchBar> {
	}

	public SearchBar() {
		initWidget(uiBinder.createAndBindUi(this));

		searchCriteria.addKeyUpHandler(new KeyUpHandler() {

			@Override
			public void onKeyUp(KeyUpEvent event) {
				handleChange();

			}
		});

	}

	protected void handleChange() {
		SearchBarEvent.fire(this, searchCriteria.getText());

	}

}

All pretty basic, we listen to KeyUp event on the textinput (listening to keypress will result in the text before the change, similar to KeyDown event in Flex), and in the handler we dispatch the custom event with the custom component as the source and the search criteria.

Now, we need to define the SearchBarEvent custom event:

public class SearchBarEvent extends GwtEvent<Handler> {

	public static Type<Handler> TYPE = new Type<Handler>();
	private String criteria;

	public SearchBarEvent(String criteria) {
		this.criteria = criteria;
	}

	public String getCriteria() {
		return criteria;
	}

	@Override
	protected void dispatch(Handler handler) {
		handler.onEvent(this);
	}

	@Override
	public Type<Handler> getAssociatedType() {
		return TYPE;
	}

	public static Type<Handler> getType() {
		return TYPE;
	}

	public static void fire(HasHandlers source, String criteria) {
		source.fireEvent(new SearchBarEvent(criteria));
	}
}

Again, pretty standard stuff…

So, to recap – every time the user presses a key on the keyboard, the custom event is fired with the search criteria, which is the text currently typed in the text input.

Now, the last piece of the puzzle will be to define someone as the listener for that event. In this case it will be the component that contains the GXT Grid, but it could be anything else in your code. The snippets follow…

First, we need to add the SearchBar to the view. Using uiBinder, it’s as simple as:

		<f:SearchBar ui:field="searchBar"/>

Again, note it is defined with ‘ui:field’ so we’ll be able to access it from the view code.

Now, in the grid containing view, there are a few things we need to do.

First, we should add the UiField annotation:

	@UiField
	SearchBar searchBar;

Second, we should define the handler for the custom event. This can be done in the constructor, after the grid has been initialized:

		searchBar.addHandler(this, SearchBarEvent.TYPE);

Since we defined the handler as ‘this’, the view should implement the EventHandler interface.

For code reuse, I created an extended interface named Handler which defines a single method: onEvent. This allows for more generic code:

public interface Handler<E extends GwtEvent<Handler>> extends EventHandler {
    void onEvent(E event);
}

And so, the view now need to implement the Handler interface above:

.... implements Handler<GwtEvent<Handler>>

This means we now need to implement the onEvent method defined in the interface:

	@Override
	public void onEvent(GwtEvent<Handler> event) {
		if (event instanceof SearchBarEvent) {
			final String criteria = ((SearchBarEvent) event).getCriteria();
			grid.getStore().removeFilters();
			grid.getStore().setEnableFilters(true);
			grid.getStore().addFilter(new StoreFilter<DesignMetadataDto>() {

				@Override
				public boolean select(Store<DesignMetadataDto> store, DesignMetadataDto parent,
						DesignMetadataDto item) {

					if (item.getName().contains(criteria)) {
						return true;
					}

					return false;
				}
			});

		}

	}

This is where the ‘magic’ happens. Since the onEvent is a generic method and might be used for other events as well, first we need to verify the event type. The code that follows is specific to GXT’s grid and out of scope here, but basically, you apply the criteria as a filter to the grid’s data store object. It is basically very similar to Flex’s filter function.

Hope this helps someone out there,

Until next time, happy coding!

Sefi

Leave a comment