GWT SuggestBox backed by DTO Model
In this post I am writing a tutorial on how to use the Google Web Toolkit SuggestBox when you want it to provide Data Transfer Objects (DTO’s) suggestions.
According to GWT’s official docs you can Associate DTO objects with SuggestBox suggestions but I struggled to find any guiding tutorials on how to achieve this.
Having said this I have to give credit to Lombardi who went someway to helping me to work out how to do this. Good explanations are posted on their blog.
First of all lets make a simple Data Transfer Object
import com.google.gwt.user.client.rpc.IsSerializable; public class PersonDTO implements IsSerializable { private int id = 0; private String firstname = null; private String lastname = null; /** * @return the firstname */ public String getFirstname() { return firstname; } /** * @param firstname the firstname to set */ public void setFirstname(String firstname) { this.firstname = firstname; } /** * @return the lastname */ public String getLastname() { return lastname; } /** * @param lastname the lastname to set */ public void setLastname(String lastname) { this.lastname = lastname; } /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } }
Thats the DTO object I’ll use throughout the tutorial.
Now we can make our Suggestion object as the following:
import com.google.gwt.user.client.ui.MultiWordSuggestOracle.MultiWordSuggestion; /** * @author James Heggs - jheggs@axonbirch.com * */ public class PersonMultiWordSuggestion extends MultiWordSuggestion { private PersonDTO personDTO = null; public PersonMultiWordSuggestion(PersonDTO user) { super(user.getFirstname() + " " + user.getLastname(), user.getFirstname() + " " + user.getLastname() ); this.personDTO = user; } /** * @return the personDTO */ public PersonDTO getPersonDTO() { return personDTO; } }
The super() call sets up the first parameter as the replacement String and the second parameter as the display string. You can of course change these.
Next we need to make our own Suggest Oracle. This is done relatively easy. My oracle is shown below:
import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.google.gwt.user.client.ui.SuggestOracle; /** * @author James Heggs - jheggs@axonbirch.com * */ public class PersonNameSuggestOracle extends SuggestOracle { private List<PersonMultiWordSuggestion> personSuggestions = null; @Override public void requestSuggestions(Request request, Callback callback) { Response resp = new Response( matchingPeople( request.getQuery(), request.getLimit() ) ); callback.onSuggestionsReady(request, resp); } /** * * @param query The current text being entered into the suggest box * @param limit The maximum number of results to return * @return A collection of people suggestions that match. */ public Collection<PersonMultiWordSuggestion> matchingPeople(String query, int limit) { List<PersonMultiWordSuggestion> matchingPeople = new ArrayList<PersonMultiWordSuggestion>(limit); // only begin to search after the user has type two characters if ( query.length() >= 2 ) { String prefixToMatch = query.toLowerCase(); int i = 0; int s = personSuggestions.size(); // Skip forward over all the names that don't match at the beginning of the array. while (i < s && !personSuggestions.get(i).getDisplayString().toLowerCase().startsWith(prefixToMatch) ) { i++; } // Now we are at the start of the block of matching names. Add matching names till we // run out of names, stop finding matches, or have enough matches. int count = 0; while (i < s && personSuggestions.get(i).getDisplayString().toLowerCase().startsWith(prefixToMatch) && count < limit) { matchingPeople.add( personSuggestions.get(i) ); i++; count++; } } return matchingPeople; } /** * @param o * @return * @see java.util.List#add(java.lang.Object) */ public boolean add(PersonMultiWordSuggestion o) { if ( personSuggestions == null ) { personSuggestions = new ArrayList<PersonMultiWordSuggestion>(); } return personSuggestions.add(o); } /** * @param o * @return * @see java.util.List#remove(java.lang.Object) */ public boolean remove(Object o) { if ( personSuggestions != null) { return personSuggestions.remove(o); } return false; } }
I think for most developers that enough code to get going but just to clear things up the following code snippets show the use of the DTO and SuggestBox as to how it could be used. Also in the code snippets I am assuming you are familar with GWT’s RPC architecture.
First an RPC call to fetch the DTO objects from a server (Production mode could ask for persistent objects from hibernate or something similar.)
PersonNameSuggestOracle oracle = new PersonNameSuggestOracle(); SuggestBox nameSuggestBox = new SuggestBox(oracle); //fetch names MyServiceAsync serviceRPC = GWT.create(MyService.class); serviceRPC.fetchPeople(new AsyncCallback< List<PersonDTO> >() { public void onFailure(Throwable caught) { // TODO Auto-generated method stub } public void onSuccess(List<PersonDTO> results) { if ( results != null && results.size() > 0 ) { for ( PersonDTO person : results ) { oracle.add(new PersonMultiWordSuggestion(person) ); } } } });
Then for those interest snippet code on how to handle the selected suggestion
nameSuggestBox.addEventHandler(new SuggestionHandler() { public void onSuggestionSelected(SuggestionEvent event) { PersonMultiWordSuggestion suggestion = (PersonMultiWordSuggestion)event.getSelectedSuggestion(); // Do what you want with the suggestion and DTO // suggestion.getPersonDTO } });
Thats basically it. If people have any questions or ideas please post them as a comment in order to help others.
Hi there,
I can however that is purely the server side code and thus is completely dependant on your architecture.
For example you may or may not use Hibernate for ORM or you may just use an in memory model.
What section of the code are you struggling with?
can you post MyServiceImpl.fetchPeople() method?
i want to add a submit button. how do i get the entries keyed into the suggestionbox by user?
submitButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// box.WhatMethodtocalltogetEntriesSelectedByUser..?
}
});
Hi,
That’s absolutely no problem. You were not too slow or too late
I am writing a blogpost about how I solved the issue and I’m referencing your post. Thank you very much again.
You can see the post here: http://altuginplainenglish.blogspot.com/2010/06/gwt-suggestbox-filled-with-any-object.html
Best wishes,
Altug
Hi there,
Sorry I haven’t replied sooner.
Thank you for your comments glad you have got your issue sorted
Hello again,
I found the functionality solution. In the Oracle Class, the added items were too few. Now I changed it to “suggestions.get(i).getDisplayString().toLowerCase().contains(prefixToMatch)” and it also takes some other items in the suggestion.
Thank you very much for your blogpost.
Best wishes,
Altug
Hello,
first of all, thank you very much for your post. It helped me so much.
My problem(s) is(are) as follows: I am filling my SuggestBox which takes some objects and they it is being filled correctly. Then, if I want to use it, it always shows me one suggestion at a time, despite extending MultiWordSuggestOracle. I do not get the other suggestions match with the query. Secondly, the white space does not work for me, I always get the first letters matching. And thirdly, I do not get the bold matching query in the suggestion’s displaystring.
any idea how I can fix these?
Thanks in advance,
Altug
Hi there,
Glad you found it useful and thank you for the nice comments.
I wrote the post when GWT was on version 1.4 – I think since then they have improved the event handling so well done for sorting that out
Eggsy
Thanks very much, it’s work perfectly.
I only change
.addEventHandler(new SuggestionHandler()
to
.addSelectionHandler(new SelectionHandler()
because the first was deprecated…
thanks again!