Friday, July 4, 2008

Google Web Toolkit - Suggest Box (RPC)

The Google Web Toolkit provides many advanced features to make a web page come alive. One feature that is used commonly in AJAX webpages is a select box that has context sensitive options.

I have already covered the SuggestBox, this article will focus on retrieving your suggestion through an asynchronous Javascript call. You may want to read the following articles as this article builds off some of those concepts.

* GWT suggest box
* GWT RPC Call

To create an RPC Suggest Box you will need to do the following
* Create a Service to serve your suggestions. This service will take an input and return a collection of data.
* Create an oracle that wraps the calls to the Service

Create Suggestion RPC Service

Your client side Suggestion box will fire an onchange event when the user enters a keystroke. When this happens you want to send over the entered text and instruct the control to suggest only items that match the pattern.

To do this we need to define a Service. As we saw in
GWT RPC Call we need to define two interfaces we'll call them:
  • IQuoteService
  • IQuoteServiceAsync

public interface IQuoteService extends RemoteService {

public static class Util {
public static IQuoteServiceAsync getInstance() {
IQuoteServiceAsync instance=(IQuoteServiceAsync) GWT.create(IQuoteService.class);
ServiceDefTarget target = (ServiceDefTarget) instance;
target.setServiceEntryPoint("/GWT/quote");
return instance;
}
}

public SuggestOracle.Response getQuote(SuggestOracle.Request req);
}

public interface IQuoteServiceAsync {
public void getQuote(SuggestOracle.Request req, AsyncCallback callback);
}

Your Oracle will need to return a class with a com.google.gwt.user.client.ui.SuggestOracle.Suggestion interface. To faciliate this we create a new class called ItemSuggestion.

Note: This class must reside on the client side else you will get serialization errors when you run the app.


public class ItemSuggestion implements IsSerializable, Suggestion {

private String s;
// Required for IsSerializable to work
public ItemSuggestion() {
}

// Convenience method for creation of a suggestion
public ItemSuggestion(String s) {
this.s = s;
}

public String getDisplayString() {
return s;
}

public String getReplacementString() {
return s;
}
} // end inner class ItemSuggestion



Now define the actual service on the server side:
public class RandomQuoteService extends RemoteServiceServlet implements IQuoteService {


public SuggestOracle.Response getQuote(SuggestOracle.Request req) {

// req has request properties that you can use to perform a db search
// or some other query. Then populate the suggestions up to req.getLimit() and
// return in a SuggestOracle.Response object.
SuggestOracle.Response resp = new SuggestOracle.Response();

List<suggestion> suggestions = new ArrayList<suggestion>();
suggestions.add(new ItemSuggestion("It is a good day to die"));
suggestions.add(new ItemSuggestion("I shall return"));
suggestions.add(new ItemSuggestion("There is nothing to fear but fear itself"));

resp.setSuggestions(suggestions);
return resp;
}
}
Create Suggestion Oracle

So far we've created our service now we need to create the Oracle.

You simply extend SuggestOracle and instruct it to use the Service for its suggestions.

public class ItemSuggestOracle extends SuggestOracle {

public boolean isDisplayStringHTML() {
return true;
}

public void requestSuggestions(SuggestOracle.Request req,SuggestOracle.Callback callback) {
IQuoteService.Util.getInstance().getQuote(req, new ItemSuggestCallback(req, callback));
}

class ItemSuggestCallback implements AsyncCallback {

private SuggestOracle.Request req;
private SuggestOracle.Callback callback;

public ItemSuggestCallback(SuggestOracle.Request _req,
SuggestOracle.Callback _callback) {
req = _req;
callback = _callback;
}

public void onFailure(Throwable error) {
callback.onSuggestionsReady(req, new SuggestOracle.Response());
}

public void onSuccess(Object retValue) {
callback.onSuggestionsReady(req,
(SuggestOracle.Response) retValue);
}
}
}


And now the result of all our work, we create a new SuggestBox with the new oracle.

public void display() {

ItemSuggestOracle oracle = new ItemSuggestOracle();
SuggestBox sb = new SuggestBox(oracle);

// Add it to the root panel.
RootPanel.get().add(sb);
}


That's it! It looks like its a lot of work but its mostly plumbing and you should find its pretty easy to do once you get the hang of it.

Give it a try and leave a comment to let me know how it goes.

60 comments:

Dan said...

I tried to put this to work in GWT 1.5. One thing you leave our of your blog entry is the code for the ItemSuggestion class. It appears to be a custom Suggestion. I tried to write my own but I'm getting a serialization error -

Caused by: com.google.gwt.user.client.rpc.SerializationException: Type 'com.getrolling.cis.client.TourSuggestion' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.

developer-resource said...

Dan, I'm guessing your defining your suggestion class on the Server Side.

You must define it on the client side, then you can import it into the Server side class and it should work.

I've updated my example to hopefully make that more clear.

Hope that helps...

Anonymous said...

What a stupid article. The code doesn't even work. Test the stuff before you post it.

developer-resource said...

Anonymous, I'm sorry it didn't work for you.

It works on my machine. If you want, post what you tried and I'd be glad to help you out.

Manikandan said...

Good article.It works fine. It helped me in very important time to finish my work. Thanks.

Anonymous said...

Worked great, thanks

Mike Kienenberger said...

You neglected to mention in this post that you have to define a servlet in the web.xml file for the service

<servlet>
<servlet-name>quoteServiceServlet</servlet-name>
<servlet-class>yourpackage.server.RandomQuoteService</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>quoteServiceServlet</servlet-name>
<url-pattern>/GWT/quote</url-pattern>
</servlet-mapping>

Unknown said...
This comment has been removed by the author.
Unknown said...

Has anybody received this error:[ERROR] Uncaught exception escaped
java.lang.NullPointerException: null
at com.google.gwt.user.client.ui.
SuggestBox.showSuggestions
(SuggestBox.java:616)
at com.google.gwt.user.client.ui.
SuggestBox.access$0
SuggestBox.java:615)

at
com.google.gwt.user.client.
ui.SuggestBox$1.
onSuggestionsReady
(SuggestBox.java:386)
at
.ItemSuggestOracle
$ItemSuggestCallback.
onFailure
(ItemSuggestOracle.java:36)

Where can i download the source code?

Anonymous said...

Hallo I go in accordance this tutorial except 1 thing I give List ItemSuggestion suggestions = new ArrayList ItemSuggestion (); instead List suggestion suggestions = new ArrayList suggestion ();

App is going. But wrong. It's not select true item. screenshot> http://stargate.cnl.tuke.sk/~maly/gwt.png

I use gwt 1.6.4, where is the problem?

Anonymous said...

Hi AtlAl -- see the previous comment about servlet mapping. This will probably fix your problem if the mapping is consistent with ServiceDefTarget.setServiceEntryPoint() in your interface that extends the RemoteService.

Thanks for this post Developer Resource!

Prusch said...

Thanks for the post. I had some trouble till I realized I had put ItemSuggestOracle on the server side. Then it worked perfectly.

dianbo said...

This post helps me a lot, thx!

Seb said...

Nice tutorial. The RPCs work fine but I have the same problem like Anonymous before.

The true items where not automatically selected.
How can I solve this problem?

Kevin Jansz said...

thanks for a very helpful post. For the comments that items aren't selected - is it because the css is missing? This is what I'm using:

/* suggestions */
.gwt-SuggestBoxPopup {
border:2px solid #C3D9FF;
background-color: window;
}
.gwt-SuggestBoxPopup .item {
padding:2px;
}
.gwt-SuggestBoxPopup .item-selected {
background-color:#C3D9FF;
padding:2px;
cursor: pointer;
}

Anonymous said...

I tried this tutorial in GTW 2.0.x - RPC is working fine. :) Unfortunately the SuggestBox is not working properly. It shows all the words/strings I put into the response and it always and only selects the first entry in the popup, no matter what I type. I can select other entries manually, but I think this is not the way the SuggestBox should work?!

Unknown said...

These pictures are so impressive. I am sure my peers will enjoy these too.Property development

Unknown said...

Interesting...
---------------
facebook history

strumpfhosen said...

This is a GREAT post! I hope you not mind.I published an excerpt on the site and linked back to your own blog for people to read the full version. Thanks for your advice.

Uk Ties said...

Interesting layout on your blog. I really enjoyed reading it and also I will be back to read more in the future.

Anonymous said...

Easily, the publish is really the greatest on this laudable topic. I concur with your conclusions and will thirstily look forward to your future updates. Saying thanks will not just be sufficient, for the fantastic lucidity in your writing. I will instantly grab your rss feed to stay privy of any updates. Solid work and much success in your business enterprise!

ashtrick said...

Hi,

This was a great article.. thanks a lot..

I was trying to tweek this a little bit.. Here, when ever you type something in the box, server side call is initiated. I have restricted this to minimum of 5 letters and it works fine with the below code in ItemSuggestOracle class

public void requestSuggestions(SuggestOracle.Request req,SuggestOracle.Callback callback) {
if(req.getQuery().length() > 4){
getService().getBroadBeanLocations(req, new ItemSuggestCallback(req, callback));
}
}


Thinking over it, if I get the list of items that matches 5 letters and loaded to the SuggestBox, why do I need to call the RPC method for letters more than 5 (because they are included in the SuggestBox already). But unfortunately this does not happen.. Could you please help me on how to achieve this please.

The reason why I'm asking this is, I have got 50000 records to search on. So, searching on all these records for each character will not be a good idea. Please help...

Thanks,
-ash

Thermage chicago said...

This is a good common sense article. Very helpful to one who is just finding the resources about this part. It will certainly help educate me.

web development services said...

You got numerous positive points there. I made a search on the issue and found nearly all peoples will agree with your blog...Loving the information on this site.

Electrical Contractor Houston said...

Spot on with this write-up, I really think this website needs much more consideration. I’ll probably be again to learn way more, thanks for that info.

Lingerie Pantyhose said...

Now thats what I call a great online resource. I am gonna go ahead and share it with my friends.

Arun said...

Hi,
I am getting following exception while executing the code.
Plzz help...
com.google.gwt.user.client.rpc.SerializationException: Type 'com.mine.service.client.ItemSuggestion' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = com.infy.service.client.ItemSuggestion@8cb15b

Mady-Censored said...

Works Perfectly Fine... awesome one...

Mady-Censored said...

i have changes my class this way

public class ItemSuggestOracle extends SuggestOracle {

public boolean isDisplayStringHTML() {
return true;
}

public void requestSuggestions(SuggestOracle.Request req,SuggestOracle.Callback callback) {
final IQuoteServiceAsync service = GWT.create(IQuoteService.class);
service.getQuote(req, new ItemSuggestCallback(req, callback));
}

class ItemSuggestCallback implements AsyncCallback {

private SuggestOracle.Request req;
private SuggestOracle.Callback callback;

public ItemSuggestCallback(SuggestOracle.Request _req,
SuggestOracle.Callback _callback) {
req = _req;
callback = _callback;
}

public void onFailure(Throwable error) {
callback.onSuggestionsReady(req, new SuggestOracle.Response());
}

public void onSuccess(Object retValue) {
callback.onSuggestionsReady(req,
(SuggestOracle.Response) retValue);
}
}
}

Mady-Censored said...

and

@RemoteServiceRelativePath("rpc/searchUserService")
public interface IQuoteService extends RemoteService {

public SuggestOracle.Response getQuote(SuggestOracle.Request req);

}

Tango kurs said...

Interesting and important information. It is really beneficial for us. Thanks

Cheap penny auctions sites said...

Thank you, thats very interesting information. I need to share with my friends.

Fulton Septic Cleaning said...

I found your website perfect for my needs. It contains wonderful and helpful posts. I have read most of them and learned a lot from them. You are doing some great work. Thank you for making such a nice website.

Fort Lauderdale criminal lawyer said...

Interesting layout on your blog. I really enjoyed reading it and also I will be back to read more in the future.

Pantyhose said...

just couldn’t leave your website before telling you that we really enjoyed the quality information you offer to your visitors… Will be back often to check up on new posts.

Fort Lauderdale criminal lawyer said...

This Blog is going places, the people, the layout, amazing to see such dedication and focus.

SEO Services said...

We're a group of volunteers and opening a new scheme in our community. Your web site provided us with valuable information to work on. You have done an impressive job and our whole community will be grateful to you.

RFA52S said...

Thanks you. Very good post.Unless they can offer a really compelling reason for users to come back, it will be the next Bebo, MySpace

Zolpidem No Prescription said...

It is an interesting post, and really appreciable. The clearness in your post is simply spectacular and i can assume you are an expert on this subject. I will grap each and every information and refer to my friends also.

deep cycle battery said...

Wow, Fantastic Blog, it’s so helpful to me, and your blog is very good,
I’ve learned a lot from your blog here, Keep on going, my friend, I will keep an eye on it,

where to buy essential oils said...

I found your website perfect for my needs. It contains wonderful and helpful posts. I have read most of them and learned a lot from them. You are doing some great work. Thank you for making such a nice website.

custom software said...

great job on the "Google Web Toolkit - Suggest Box (RPC)"

Training Tampa said...

I have to say, every time I come to #hostname you have another interesting post to read. A friend of mine was telling me about this topic a few weeks ago, so I think I'll e-mail them the link here and see what they say.

Leicester fridges said...

I just couldn’t leave your website before telling you that we really enjoyed the quality information you offer to your visitors… Will be back often to check up on new posts.

Vancouver Internet Marketing said...

This is a good common sense Blog. Very helpful to one who is just finding the resources about this part. It will certainly help educate me.

Sign Installation said...

Interesting and important information. It is really beneficial for us. Thanks

blackberry developer said...

I think the code needs some repairment because some pieces of it do not work properly. SO it'd be great if the author will reconsider it as it might be really useful.

Jake Gyllenhall workout said...

Thanks for such a very informative information. very nice I thing to get more information like this...

Channing Tatum workout said...

Easily, the publish is really the greatest on this laudable topic. I concur with your conclusions and will thirstily look forward to your future updates. Saying thanks will not just be sufficient, for the fantastic lucidity in your writing. I will instantly grab your rss feed to stay privy of any updates. Solid work and much success in your business enterprise!

Anonymous said...

Just fetch data if leng=5, not lenght> 4

Luxury British Watches said...

Nice job. Personally I like this article. I think it would be more and more helpful for all those people are want to drive their career in SEO field.

Web development Company said...

This is a nice web site. Good fresh interface and nice informative articles. I will be coming back soon, thanks for the great article.

Aanya Dsouza said...

I am very much impressed with this blog. I am very much interested in magento web development also. But this is something different for getting more information.

click here said...

This website was... how do I say it? Relevant!! Finally I have found something which helped me. Thank you!
get information

Best Enterprise application said...

Thanks for the blog. It's great i like it.

Unknown said...

Assuming in the last quarter of 2014 APL Logistics generates revenues north tiffany boxing day sale of $400m – which is achievable based on weekly revenues of more than $30m in recent quarters – its annual revenues will stand at about $1.6bn. tiffany and co australia sale The unit has fared pretty well in recent times, having grown in all its markets: Asia, Middle East, Americas and Europe. Assuming steady operating margins, Swarovski australia sale it may generate Ebitda of about $80m in 2014, up from $76m in 2013.

Anonymous said...

Lovely blog having such a valuable content!!!.

Frank Roberts said...

I regret to comment on this blog but don't really have other options left have to try it for some reason.
Regards
Frank
hire offshore developers

jennifer smith said...

Decent Job. By and by I like this article. I think it would be increasingly useful for every one of those individuals are need to drive their vocation in SEO field.

hellanadam said...

Being new to the blogging world I feel like there is still so much to learn. Your tips helped to clarify a few things for me as well as giving..
ios App Development Company
Android App Development Company
Mobile App Development Company