Spring 3 MVC – Exemplo de Auto-completar com JQuery & JSON

Nesse artigo, iremos implementar o recurso de Autocompletar em uma aplicação Spring MVC usando o JQuery. O Auto-completar é um recurso visto em quase todas as boas aplicações web. Ela permite que o usuário selecione os valores apropriados em uma lista de itens. A adiçao desse recurso é recomendável se o campo possuir múltiplos (> 20 a 25) valores.
Nossos requisitos são simples. Teremos dois campos Country e Technologies. Os dois campos terão o recurso de auto-completar de forma que o usuário seja capaz de selecionar valores em uma lista de países e tecnologias. O campo país terá apenas um valor, mas o campo tecnologia poderá ter múltiplos valores separados por virgula (,).

O que iremos precisar

Antes de começarmos nosso exemplo, precisaremos de algumas ferramentas.

  1. JDK 1.5 ou superior (download)
  2. Tomcat 5.x ou superior ou qualquer outro contêiner (Glassfish, JBoss, Websphere, Weblogic etc) (download)
  3. Eclipse 3.2.x ou superior (download)
  4. JQuery UI (Auto-completar) (download)
  5. Arquivos JAR do Spring 3.0 MVC: (download). A seguir uma lista dos arquivos JAR necessários para essa aplicação.
    • jstl-1.2.jar
    • org.springframework.asm-3.0.1.RELEASE-A.jar
    • org.springframework.beans-3.0.1.RELEASE-A.jar
    • org.springframework.context-3.0.1.RELEASE-A.jar
    • org.springframework.core-3.0.1.RELEASE-A.jar
    • org.springframework.expression-3.0.1.RELEASE-A.jar
    • org.springframework.web.servlet-3.0.1.RELEASE-A.jar
    • org.springframework.web-3.0.1.RELEASE-A.jar
    • commons-logging-1.0.4.jar
    • commons-beanutils-1.8.0.jar
    • commons-digester-2.0.jar
    • jackson-core-asl-1.9.7.jar
    • jackson-mapper-asl-1.9.7.jar

Observe que dependendo da versão atual do Sptring MVC, os números de versão acima podem varias. Observe também que precisamos dos arquivos jackson mapper e jackson core. Esses arquivos são necessários para gerar arquivos JSON a partir de nosso controller Spring MVC.

Começando

vamos começar a desenvolver a nossa aplicação baseada no Spring 3.0 MVC. Abra o Eclipse e acesse File -> New -> Project selecionando Dynamic Web Project na tela que aparece.

Após selecionar Dynamic Web Project, pressione Next.

Escreve um nome para o projeto. Por exemplo, SpringMVC_Autocomplete. Após ter feito isso, selecione o ambiente alvo (ex.: Apache Tomcat v6.0). Isso é feito para poder rodar o projeto de dentro do ambiente Eclipse. Depois disso pressione Finish.
Uma vez que o projeto tenha sido criado, você pode visualizar sua estrutura no Project Explorer. Essa é a forma que o projeto deve parecer assim que tivermos finalizado o tutorial e adicionado todo o código fonte.
springmvc-autocomplete-project-structure
Agora copie todos os arquivos JAR necessáriso no diretório WebContent > WEB-INF > lib. Crie esse diretório caso não exista.

Um banco de dados de teste

Normalmente você precisará de um banco de dados de onde irá puxar os valores necessários para o Auto-completar. Mas por questões de simplicidade, criaremos uma classe java DummyDB.
Após o projeto ter sido criado, crie um pacote net.viralpatel.springmvc.autocomplete e um arquivo DummyDB.java. DummyDB.java é a classe que simulará a conexão com o banco de dados e fornecerá os dados para nosso exemplo.
Arquivo: /src/net/viralpatel/springmvc/autocomplete/DummyDB.java

package net.viralpatel.spring.autocomplete;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class DummyDB {
    private List<String> countries;
    private List<String> tags;
    public DummyDB() {
        String data = "Afghanistan, Albania, Algeria, Andorra, Angola, Antigua & Deps,"+
                "United Kingdom,United States,Uruguay,Uzbekistan,Vanuatu,Vatican City,Venezuela,Vietnam,Yemen,Zambia,Zimbabwe";
        countries = new ArrayList<String>();
        StringTokenizer st = new StringTokenizer(data, ",");
        //Parse the country CSV list and set as Array
        while(st.hasMoreTokens()) {
            countries.add(st.nextToken().trim());
        }
        String strTags = "SharePoint, Spring, Struts, Java, JQuery, ASP, PHP, JavaScript, MySQL, ASP, .NET";
        tags = new ArrayList<String>();
        StringTokenizer st2 = new StringTokenizer(strTags, ",");
        //Parse the tags CSV list and set as Array
        while(st2.hasMoreTokens()) {
            tags.add(st2.nextToken().trim());
        }
    }
    public List<String> getCountryList(String query) {
        String country = null;
        query = query.toLowerCase();
        List<String> matched = new ArrayList<String>();
        for(int i=0; i < countries.size(); i++) {
            country = countries.get(i).toLowerCase();
            if(country.startsWith(query)) {
                matched.add(countries.get(i));
            }
        }
        return matched;
    }
    public List<String> getTechList(String query) {
        String country = null;
        query = query.toLowerCase();
        List<String> matched = new ArrayList<String>();
        for(int i=0; i < tags.size(); i++) {
            country = tags.get(i).toLowerCase();
            if(country.startsWith(query)) {
                matched.add(tags.get(i));
            }
        }
        return matched;
    }
}

DummyDB.java contém a lista de todos os países e tecnologias em uma String separados por vírgulas e métodos getCountryList() e getTechList() que retornarão uma lista de países e tecnologias que começem com a String passada como argumento. Dessa forma se passarmos “IN” a esse método, ele retornará todos os países começados por IN.
Você pode alterar esse código para adicionar a implementação orientada a acessar o banco de dados. Apenas um simples "SELECT * FROM <table> WHERE country LIKE " query  irá servir aos propósitos da aplicação.
Agora criaremos o controller do SpringMVC que retornará uma saída JSON para o Auto-completar.

O Controller do Spring MVC

A classe do controller do Spring MVC irá processar a requisição e retorna uma saída JSON. Para isso crie uma classe  UserController.java sob o pacote net.viralpatel.springmvc.autocomplete.

package net.viralpatel.spring.autocomplete;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class UserController {
    private static DummyDB dummyDB = new DummyDB();
    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public ModelAndView index() {
        User userForm = new User();
        return new ModelAndView("user", "userForm", userForm);
    }
    @RequestMapping(value = "/get_country_list",
                    method = RequestMethod.GET,
                    headers="Accept=*/*")
    public @ResponseBody List<String> getCountryList(@RequestParam("term") String query) {
        List<String> countryList = dummyDB.getCountryList(query);
        return countryList;
    }
    @RequestMapping(value = "/get_tech_list",
                    method = RequestMethod.GET,
                    headers="Accept=*/*")
    public @ResponseBody List<String> getTechList(@RequestParam("term") String query) {
        List<String> countryList = dummyDB.getTechList(query);
        return countryList;
    }
}

Agora usaremos a anotação @ResponseBody dos métodos getCountryList() egetTechList(). O Spring MVC converte o retorno dos métodos que em nosso caso é List em dados JSON.
A seguir o contéudo do arquivo spring-servlet.xml.

<?xml  version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="net.viralpatel.spring.autocomplete" />
    <mvc:annotation-driven />
    <bean id="jspViewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass"
            value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

A tag é necessária aqui. Isso permite que o Spring processe anotações como @ResponseBody.
A classe User.java abaixo é necessária apenas para ligar um formulário com o JSP. No nosso exemplo, ela não é necessária. Mas para o bom uso do Spring MVC iremos adiciona-la.
Arquivo: /src/net/viralpatel/springmvc/autocomplete/User.java

package net.viralpatel.spring.autocomplete;
public class User {
    private String name;
    private String country;
    private String technologies;
    //Getter and Setter methods
}

O View JSP

Agora adicionaremos o arquivo JSP que irá renderizar o formulário User. Iremos adicionar também um index.jsp que irá redirecionar para a requisição apropriada.
Arquivo: /WebContent/index.jsp

<jsp:forward page="index.html"></jsp:forward>

Arquivo: /WebContent/WEB-INF/jsp/user.jsp

<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
    <title>Spring MVC Autocomplete with JQuery & JSON example</title>
    <link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" />
    <script type="text/javascript"
        src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script type="text/javascript"
        src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
</head>
<body>
<h2>Spring MVC Autocomplete with JQuery & JSON example</h2>
<form:form method="post" action="save.html" modelAttribute="userForm">
<table>
    <tr>
        <th>Name</th>
        <td><form:input path="name" /></td>
    </tr>
    <tr>
        <th>Country</th>
        <td><form:input path="country" id="country" /></td>
    </tr>
    <tr>
        <th>Technologies</th>
        <td><form:input path="technologies" id="technologies" /></td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="submit" value="Save" />
            <input type="reset" value="Reset" />
        </td>
    </tr>
</table>
<br />
</form:form>
<script type="text/javascript">
function split(val) {
    return val.split(/,\s*/);
}
function extractLast(term) {
    return split(term).pop();
}
$(document).ready(function() {
    $( "#country" ).autocomplete({
        source: '${pageContext. request. contextPath}/get_country_list.html'
    });
    $( "#technologies").autocomplete({
        source: function (request, response) {
            $.getJSON("${pageContext. request. contextPath}/get_tech_list.html", {
                term: extractLast(request.term)
            }, response);
        },
        search: function () {
            // custom minLength
            var term = extractLast(this.value);
            if (term.length < 1) {
                return false;
            }
        },
        focus: function () {
            // prevent value inserted on focus
            return false;
        },
        select: function (event, ui) {
            var terms = split(this.value);
            // remove the current input
            terms.pop();
            // add the selected item
            terms.push(ui.item.value);
            // add placeholder to get the comma-and-space at the end
            terms.push("");
            this.value = terms.join(", ");
            return false;
        }
    });
});
</script>
</body>
</html>

Verifique o arquivo JSP acima. Adicionamos campos INPUT para Country e tecnology. Também usamos$().autocomplete() para ativar o recurso de auto-completar. Para o país o uso é bem direto: $( "#country" ).autocomplete() mas para tecnologias fizemos alguns processamentos e divisões na entrada. Isso se deve ao fato de que podemos ter múltiplos valores para o campo, separados por vírgula.

Isso é tudo pessoal

Você pode agora executar a aplicação para visualizar o resultado. Aqui, assume-se que você já configurou o Tomcat no Eclipse. Tudo que precisa fazer é:
Abra a aba Server em Windows > Show View > Server. Clique com o botão direito do mouse nessa área e selecione New > Server para adicionar os detalhes do servidor.
Para executar o projeto, clique com o botão direito no nome do projeto e selecione Run as > Run on Server (Shortcut: Alt+Shift+X, R)
springmvc-autocomplete-example
springmvc-autocomplete-multi-value

Baixar o código fonte

SpringMVC_Autocomplete.zip (4.2 MB)
Traduzido de viralpatel.net