Grails Autocomplete via jQuery UI and AJAX

In this tutorial, I will teach you how to create a field with auto-complete functions using jQuery UI and AJAX, but before we get started please take note of the stuff I'm using and their versions:
If you have not installed any of the plugins, please do so before starting out. Okay.. You done? Let's start! Suppose I have a domain called country and I have a text field where a list of countries come out based on the user's input. Kinda like the screenshot below:

If you want a live demo, click on the link here and see it for yourself!
First, I will embed the jQuery and jQuery UI in my gsp files.
< r:require module="jquery-ui" />
Next, I'll be making my Country domain:
package com.icodeya

class Country {

    String name

    static constraints = {
        name(blank: false, unique: true)
    }
}
In my controller, I will create an action that will return all the countries and render them in JSON format.
def getAllCountries(){
        def countries = Country.list()
        def response = []

        countries.each{
            response << "${it.name}"
        }

        render response as JSON
    }
Of course, I'll be sure to import the necessary stuff for JSON conversion to work.
import grails.converters.JSON
Next, I'll create a textfield where the user enters his input. Take note of "id='country_textField'" because I'll be using it to identify the text field later in my script.
< input style="float: right; margin: 0px 10px 10px 0px;" 
type="text" name="country" id="country_textField" 
value="" placeholder="Enter country..." />
Next, I'll be creating a script(I'll call it util.js) that will load all the countries whenever the page is loaded. This can easily be achieved via AJAX! I will simply create a url by using "/projectName/controller/action" which in this case is "/autoCompleteTut/country/getAllCountries" so that I can access the action that I created in my controller earlier. If the controller successfully returns a response, then my script searches for a text field with the id "country_textField" and transforms it into a textfield with auto complete features! :)
$(document).ready(function() {
    $.ajax({
        type: "GET",
        url: "/autoCompleteTut/country/getAllCountries",
        success : function(response) {

            $("#country_textField").autocomplete({
                source: response
            });
        }
    });

});

So, what if I want to get the id of the item selected?
I'm really happy somebody brought this up! Well, here's what we want to happen (at least the way I see it). First, we create a hiddenfield where the id of the country is supposed to be placed. When a user selects a country, the hiddenfield simply gets updated with the appropriate id. Fortunately, jQuery's autocomplete has a function for this called: 'select'. I actually created a separate branch in bitbucket for this section, go check it out here! Let's get started!

Create a hidden field next to the textbox!
< input style="float: right; margin: 0px 10px 10px 0px;" type="text" 
name="country" id="country_textField" 
value="" placeholder="Enter country..." / >
< input type="hidden" id="country_id" name="country_id" value="" />
In our controller, we simply return a list of countries as JSON.
def getAllCountries(){
        def countries = Country.list()

        render countries as JSON
}
Next, in our javascript file, we create a map from the returned response from the controller and store it in a variable.
var data =
                $.map(response, function(item){
                  console.log("id: " + item.id);
                  console.log("name: " + item.name);
                    return{
                        id: item.id,
                        value: item.name
                    }
                });
It will then be used in the autocomplete method's select function, where we change the value of the hiddenfield to the country's id. :)
$("#country_textField").autocomplete({
                source: data,
                select: function (event, ui){
                    console.log("selected id:" + ui.item.id);
                    console.log("selected name:" + ui.item.value);

                    //when a country is selected(ie: type China and press enter),
                    //change the value of hidden field to the country's id.
                    $('#country_id').val(ui.item.id);
                }
            });
The whole javascript file should look something like this:
$(document).ready(function() {
    $.ajax({
        type: "GET",
        url: "/autoCompleteTut/country/getAllCountries",
        dataType: "json",
        success : function(response) {
            //Create a map.
            var data =
                $.map(response, function(item){
                  console.log("id: " + item.id);
                  console.log("name: " + item.name);
                    return{
                        id: item.id,
                        value: item.name
                    }
                });


            $("#country_textField").autocomplete({
                source: data,
                select: function (event, ui){
                    console.log("selected id:" + ui.item.id);
                    console.log("selected name:" + ui.item.value);

                    //when a country is selected(ie: type China and press enter),
                    //change the value of hidden field to the country's id.
                    $('#country_id').val(ui.item.id);
                }
            });

        }
    });

});
That's it really! I hope this helps. :) Wanna checkout the full source code? Clone/Download it from Bitbucket! :)

Written by

I am a software developer with 2 years of experience. I like cats. I like going to hackathons for free food and temporary lodging. I want to be a polyglot. Currently, I can speak languages like English, Korean, Java (it counts right?), Groovy, Ruby and C#. I aim to learn German as well.

15 (mga) puna:

  1. Very interesting! But I have a question. Let's say Country domain class has "Name", "Zip Code" and "Population" attributes. When I type the name of the country the suggestion list is displayed and when I select one country the zip code and population must be retrieved and shown in other text fields. How can I do this?

    ReplyDelete
  2. @Manuel Calles
    Hmm.. I think you can refer to JQuery's website for that.
    Perhaps something like this?

    http://jqueryui.com/autocomplete/#remote

    ReplyDelete
  3. Hi icodeya,

    I am trying to implement your example in my grails application but got stuck with js function. It throws control out of function around this $.ajax({ line . any idea why this is happening.

    ReplyDelete
  4. Hi Vieenay!

    I'm sorry, I don't quite get how that happened. Could you elaborate further?
    What does the console say?

    ReplyDelete
  5. This was very helpful, thanks so much! One question i have though is if I add a few more countries that are similar in name and then try to search for one the result list won't return the closest match on the top of the list. How would i have to go about trying to improve that search result?

    Thanks, again!

    ReplyDelete
  6. This was very helpful, thanks so much! One question i have though is if I add a few more countries that are similar in name and then try to search for one the result list won't return the closest match on the top of the list. How would i have to go about trying to improve that search result?

    Thanks, again!

    ReplyDelete
  7. What do you mean? The autocomplete works fine even if there are two countries with similar names (for example, 'china' and 'chinar'. search for china, china and chinar are on the list.) My demo doesn't cover the part where you press "enter" and the list of countries with similar names come up. You'll have to figure that out for yourself. ;)

    How does your code look like in the controller? Maybe, instead of Country.findByName('countryname') you can use Country.findByNameLike('countryname'). refer here: http://grails.org/doc/2.1.0/ref/Domain%20Classes/findBy.html

    ReplyDelete
  8. Hi there, your code is very helpful and easy to understand. I have replicated this in my project. Everything seems in place but the text field is not populating any thing as I type. I checked the pagesource of your page(http://autocompletesample.icodeya.cloudbees.net/country/list) and then mine. Your page source has reference to <script src="/static/js/util.js", but not in mine. Can you please tell me where I might be doing wrong.

    Thanks

    ReplyDelete
  9. Hi there @Anonymous! :)

    Where did you place the last piece of code? I'm referring to the code above which starts with "$(document).ready(function() { ... ".

    In my app, I created a util.js, pasted the last piece of code there, and linked it to my page via:

    ' < g:javascript library="util"/>'

    If you didn't link it, then obviously, the autocomplete wouldn't work.
    For a good reference, you can look at my source code here:
    https://bitbucket.org/icodeya/autocompletetut

    Hope it helps. :)

    ReplyDelete
  10. It is good and easy ... But I have same query as asked by Manuel Calles .... I want to display a group of fields as Display members but want to save a different id as value member so that I can pass id instead while I submit the form ... Any help would be appreciated

    ReplyDelete
    Replies
    1. Hi,

      I'm really sorry for the late reply. I had a trip to Malaysia last week and I didn't bring my laptop with me. I created a separate branch in bitbucket where you might find the answer.
      https://bitbucket.org/icodeya/autocompletetut/commits/94f9d5b23c52a422a40dfcdc17f5c7058f5cb3c8?at=master

      Initially, I created a hidden field, and this is where the id of the country goes. So what happens is, when a user selects a country (ie: types China and presses enter) the "select function" is triggered and the hidden field is filled up with the appropriate country id. That's it really.

      I'll be adding this up in the tutorial since, lots of people seems to be asking. :)

      I referred to this:
      http://jqueryui.com/autocomplete/#remote

      Delete
  11. Perfect :) ... Thanks

    One Small Query though pertaining to the display. I have two classes as mentioned below:

    First Class:

    class State {
    String stateName
    String toString() {"${this.state}"}

    static constraints = {
    state(blank:false, unique:true)
    }
    }



    Second Class:

    class District {
    String districtName
    State state
    String toString() {"${this.district}"}

    static constraints = {
    stateName(blank:false)
    district(blank:false, nullable:false)

    }
    }


    I am applying autocomplete on districtName of class District but I want to display stateName appended to it as well in the autocomplete dropdown. Since I can access to id of state in district table, it is showing me undefined object while I try to display item.state which is an instance of District class. I hope you got the question :) ?

    ReplyDelete
    Replies
    1. Just guessing, I'm on mobile right now. Instead of item.state, try item.state.stateName ?

      Delete
  12. I have done it already ... Thanks though .... I did the same thing ... But I had to use JSON.use("deep") block to get deep json and then only I could access items of superclass or parent class... Thanks again.

    def jsn
    JSON.use("deep"){
    def c = (State) State.get(params.state.id)
    def j = c as JSON
    jsn = j.toString()
    }

    ReplyDelete
    Replies
    1. Awesome to hear that it's finally worked for you. :)

      Delete

 

© 2013 icodeya. All rights resevered.Designed by Templateism

Back To Top