JQuery Ajax auto-complete with Spring MVC and REST

Objective

Implement auto complete feature such that when we enter two characters in a text field a drop-down should appear with the remaining content which starts with those characters.

Purpose

Learn how auto complete work with stand alone data or with remote data coming from a distant server in conjunction with Spring MVC framework or a REST call.

Things Needed

Jquery and knowledge of Spring Controllers along with basic understanding of Spring MVC.

Approach

To achieve this we implement city auto complete feature meaning when we enter two characters then all the cities starting with those two characters will appear and so on.

1.  Auto complete with Local Data

Most simple form of Auto complete example with stand alone data meaning the data is within the local javascript function. If you copy this code and save it as an html file then you can see it working. Notice that we are using a little older version of JQuery jquery-1.6.2.js The reason is that JQuery-UI must be following up with the changes in latest revisions etc. See the workable demo in cloud foundry http://autocomplete.cloudfoundry.com/ajaxLocal.html

Another point to note is the ajax call

 $(function() {$( "#city" )

.autocomplete({source: ["Minneapolis", "Minnehaha"]});

}); 

we are binding the input text field “city” with the ajax call. The $ here says that its a callback function.


<html>

<head>

<link rel="stylesheet" href="js/jquery-ui-1.8.16/themes/base/jquery-ui.css" type="text/css" />

<link rel="stylesheet" href="js/jquery-ui-1.8.16/themes/base/jquery.ui.autocomplete.css" type="text/css" />

<script src="js/jquery-ui-1.8.16/jquery-1.6.2.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery-ui.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.core.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.position.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.widget.js"></script></head>

<script>

$(function() {$( "#city" )

.autocomplete({source: ["Minneapolis", "Minnehaha"]});

});

</script>

<input id="city" />

</html>

2.  Auto complete with Data from Local Spring controller

Next form of Auto complete example is with calling a spring controller to provide you dynamic result sets. We created a Spring MVC controller called ZipcodeController.java with request mapping url “/ajaxcitySearchGeneral” here is how the code look. see the workable demo in cloud foundry http://autocomplete.cloudfoundry.com/ajaxToLocalController.html

@Controller

public class ZipcodeController

{

/**

* This method will return same set of zipcodes all the time.

*

* @return Map<String, List<ZipcodeDetails>> represents a JSON name value pair

*/

@RequestMapping(value="/ajaxcitySearchGeneral", method = RequestMethod.GET)

@ResponseBody

public Map<String, List<ZipcodeDetails>> getZipcodes()

{

List<ZipcodeDetails> zipcodes = new ArrayList<ZipcodeDetails>(Arrays.asList(new ZipcodeDetails("55401", "Minneapolis", "MN"), new ZipcodeDetails("1949", "Middleton", "MA"), new ZipcodeDetails("2055", "MINOT", "MA")));

Map<String, List<ZipcodeDetails>> zipMap = new HashMap<String, List<ZipcodeDetails>>();

zipMap.put("zipcodes", zipcodes);

return zipMap;

}

}

Here we are hard coding the data in the spring controller but as you can see we can make a call to persistence layer and get dynamic data. The java script and html for this will look like below code.


<!--

! Spinach Software & Consulting LLC

! Copyright 2011 by Spinach Software & Consulting LLC. All rights reserved.

-->

<html>

<head>

<link rel="stylesheet" href="js/jquery-ui-1.8.16/themes/base/jquery-ui.css" type="text/css" />

<script src="js/jquery-ui-1.8.16/jquery-1.6.2.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery-ui.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.core.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.position.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.widget.js"></script>

</head>

<script>

$(function() {

$( "#city" ).autocomplete({

source: function( request, response ) {

$.ajax({

url: "/ajaxcitySearchGeneral",

dataType: "json",

data: {

maxRows: 6,

startsWith: request.term

},

success: function( data ) {

response( $.map( data.zipcodes, function( item ) {

return {

label: item.cityName + ", " +item.zipcodeId + ", " + item.state,

value: item.cityName

}

}));

}

});

},

minLength: 1,

open: function() {

$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );

},

close: function() {

$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );

}

});

});

</script>

 

<div class="demo">

<div class="ui-widget">

<label for="city">Search City: </label>

<input id="city" />

</div>

</div>

</html>

3.  Auto complete with Data from Remote REST service

This is by far the most useful one since most of the time the REST service will be on a separate server e.g. in this one we will use a REST service located at http://ziplocator.cloudfoundry.com/rest/startsWithCity/ with two query parameters “startsWith” and “maxRows”  so lets say if we are looking for a city that starts with letter “min” then here is the url http://ziplocator.cloudfoundry.com/rest/startsWithCity/?startsWith=min&maxRows=5

Now all we have to do is consume this json result in our html. This is how it looks


<!--

! Spinach Software & Consulting LLC

! Copyright 2011 by Spinach Software & Consulting LLC. All rights reserved.

-->

<html>

<head>

<link rel="stylesheet" href="js/jquery-ui-1.8.16/themes/base/jquery-ui.css" type="text/css" />

<script src="js/jquery-ui-1.8.16/jquery-1.6.2.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery-ui.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.core.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.position.js"></script>

<script type="text/javascript" src="js/jquery-ui-1.8.16/ui/jquery.ui.widget.js"></script>

</head>

 

<script>

$(function() {

$( "#city" ).autocomplete({

source: function( request, response ) {

$.ajax({

url: "http://ziplocator.cloudfoundry.com/rest/startsWithCity/",

dataType: "json",

 

data: {

maxRows: 10,

startsWith: request.term

},

success: function( data ) {

response( $.map( data.zipcodes, function( item ) {

 

return {

label: item.cityName + ", " +item.zipcodeId + ", " + item.state,

value: item.cityName

}

}));

}

});

},

minLength: 1,

open: function() {

$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );

},

close: function() {

$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );

}

});

});

</script>

 

<div class="demo">

<div class="ui-widget">

<label for="city">Search City: </label>

<input id="city" />

</div>

</div>

</html>

See the  complete end to end demo of this in cloud foundry here http://autocomplete.cloudfoundry.com/ajaxToRemoteREST.html 

Conculsion

Most important concept is to see that JQuery library and JQuery-UI are two separate things. JQuery-UI is specifically made for user interface related stuff and is built on top of JQuery. Latest version of JQuery-UI can be downloaded from http://jqueryui.com/

Also see a complete demo of this functionality at cloud foundry http://autocomplete.cloudfoundry.com/

Note: For the purpose of auto complete we do not need all the libraries that came by default with JQery-UI, so I trimmed it down to what is needed. Please see the complete source code in the end.

Download all code

4 thoughts on “JQuery Ajax auto-complete with Spring MVC and REST

  1. I doing everything same but not able to retrieve data using jquery… it seems spring is not able to convert it to JSON. Please help.

  2. Here is what I got when I tried to run it :

    SEVERE: A child container failed during start
    java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/wara-ws]]
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
    at java.util.concurrent.FutureTask.get(FutureTask.java:83)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:800)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    at java.lang.Thread.run(Thread.java:680)
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/wara-ws]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    … 7 more
    Caused by: java.lang.NoClassDefFoundError: org/springframework/web/context/ConfigurableWebEnvironment
    at java.lang.Class.getDeclaredFields0(Native Method)
    at java.lang.Class.privateGetDeclaredFields(Class.java:2300)
    at java.lang.Class.getDeclaredFields(Class.java:1745)
    at org.apache.catalina.util.Introspection.getDeclaredFields(Introspection.java:106)
    at org.apache.catalina.startup.WebAnnotationSet.loadFieldsAnnotation(WebAnnotationSet.java:261)
    at org.apache.catalina.startup.WebAnnotationSet.loadApplicationServletAnnotations(WebAnnotationSet.java:140)
    at org.apache.catalina.startup.WebAnnotationSet.loadApplicationAnnotations(WebAnnotationSet.java:67)
    at org.apache.catalina.startup.ContextConfig.applicationAnnotationsConfig(ContextConfig.java:405)
    at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:881)
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:369)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5179)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 7 more
    Caused by: java.lang.ClassNotFoundException: org.springframework.web.context.ConfigurableWebEnvironment
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1713)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1558)
    … 21 more
    Jun 5, 2013 8:32:41 PM org.apache.catalina.core.ApplicationContext log
    INFO: No Spring WebApplicationInitializer types detected on classpath
    Jun 5, 2013 8:32:41 PM org.apache.catalina.core.ContainerBase startInternal
    SEVERE: A child container failed during start
    java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
    at java.util.concurrent.FutureTask.get(FutureTask.java:83)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:684)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    at java.lang.Thread.run(Thread.java:680)
    Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:800)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 7 more
    Jun 5, 2013 8:32:41 PM org.apache.catalina.startup.Catalina start
    SEVERE: Catalina.start:
    org.apache.catalina.LifecycleException: Failed to start component [StandardServer[8005]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:684)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardService[Catalina]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 7 more
    Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 9 more
    Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    … 11 more
    Jun 5, 2013 8:32:41 PM org.apache.catalina.startup.Catalina start
    INFO: Server startup in 3453 ms

    Thanks.

Leave a Reply to Sandeep Cancel reply

Your email address will not be published. Required fields are marked *

     

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>