Use remoteCommand para comunicação entre JavaScript e JSF

Veja neste artigo como usar o componente remoteCommand do Primefaces para realizar chamadas em actions do JSF através do JavaScript.

Um componente muito útil presente no acervo de componentes do Primefaces é o “Remote Command” e neste artigo trataremos especificamente do seu uso através de um exemplo prático de seu funcionamento.

É comum possuirmos código JavaScript em nosso sistema WEB, mesmo este sendo em JSF, há diversas utilidades em usá-lo. O jQuery, por exemplo, é um framework desenvolvido em JavaScript e funciona no lado do cliente realizando validações, formatações, entre outros. Mas entramos em um problema quando precisamos realizar uma comunicação entre JavaScript e JSF, pois, imagine que precisamos passar parâmetros da resposta obtida pelo JavaScript para o JSF e salvar no banco de dados. Só conseguimos fazer isso com o Remote Command, que será a ponte de comunicação entre essas duas camadas.

Primeiramente vamos pensar em um caso hipotético e trabalhar na resolução deste, assim o entendimento será muito mais abrangente.

Estudo de Caso: Precisamos realizar a consulta de CEP's usando JavaScript e salvar a resposta dessa consulta (que contém rua, bairro, número e etc.) no banco de dados. Salvar a resposta dessa consulta pode ser interessante para sistemas que cobram por consulta (como é o caso de consultas ao SPC e SERASA), assim você poderia estipular uma taxa para cada consulta realizada em uma determinada conta de usuário.

Definido nosso estudo de caso, vamos começar a estruturar nosso projeto para receber o componente Remote Command.

Estruturando o projeto

Usamos o Maven para criar nosso projeto, assim fica mais fácil realizar a gerência de bibliotecas necessárias através do pom.xml. O arquétipo utilizado foi o maven-archetype-webapp e o nosso pom.xml possui o conteúdo presente na Listagem 1.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>br.com</groupId> <artifactId>lab</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>lab Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.omnifaces</groupId> <artifactId>omnifaces</artifactId> <version>1.11-M1</version> </dependency> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.1.6</version> </dependency> <dependency> <groupId>com.sun.faces</groupId> <artifactId>jsf-impl</artifactId> <version>2.1.6</version> </dependency> <dependency> <groupId>org.primefaces.extensions</groupId> <artifactId>primefaces-extensions</artifactId> <version>0.7.1</version> </dependency> <dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>3.5</version> </dependency> <dependency> <groupId>org.primefaces.themes</groupId> <artifactId>bootstrap</artifactId> <version>1.0.9</version> </dependency> </dependencies> <build> <finalName>lab</finalName> </build> </project>
Listagem 1. pom.xml

As bibliotecas utilizadas são:

Feito isso, já temos as bibliotecas importadas, então agora precisamos configurar o uso da classe UnmappedResourceHandler do omnifaces. Para isso, criamos uma referência a ela no arquivo faces-context.xml dentro de “src/main/webapp/WEB-INF”, como mostra a Listagem 2.

<?xml version="1.0" encoding="UTF-8"?> <faces-config version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd"> <application> <resource-handler>org.omnifaces.resourcehandler .UnmappedResourceHandler</resource-handler> </application> </faces-config>
Listagem 2. faces-config.xml

Para que nosso projeto web funcione precisamos ainda no web.xml, presente na Listagem 3.

<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID"> <display-name>LAB</display-name> <welcome-file-list> <welcome-file>index.xhtml</welcome-file> </welcome-file-list> <context-param> <param-name>primefaces.THEME</param-name> <param-value>bootstrap</param-value> </context-param> <!-- Configuracao DO JSF --> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> <url-pattern>/javax.faces.resource/*</url-pattern> </servlet-mapping> </web-app>
Listagem 3. web.xml

Nosso projeto terá o nome “LAB” e seu arquivo de “bem-vindo”, ou seja, a página inicial será a index.xhtml, que mostraremos mais à frente.

Veja que colocamos o tema padrão “bootstrap” através da configuração:

<context-param> <param-name>primefaces.THEME</param-name> <param-value>bootstrap</param-value> </context-param>

Não que isso fará alguma diferença em nossa lógica, apenas definimos por questão estética. Posteriormente configuramos o mapeamento do JSF.

Criando a consulta de CEP

Vamos começar criando a consulta de CEP em nosso arquivo JavaScript, como mostra a Listagem 4.

function consultaCep() { var cep_code = $('#cep').val(); if (cep_code.length <= 0) return; $.get("http://apps.widenet.com.br/busca-cep/api/cep.json", { code : cep_code }, function(result) { if (result.status != 1) { alert(result.message || "Houve um erro desconhecido"); return; } var cep = result.code; var estado = result.state; var cidade = result.city; var bairro = result.district; var endereco = result.address; setarValoresCEP([ { name : 'cep', value : cep }, { name : 'estado', value : estado }, { name : 'cidade', value : cidade }, { name : 'bairro', value : bairro }, { name : 'endereco', value : endereco } ]); }); }
Listagem 4. main.js

Primeiramente capturamos o valor do CEP digitado através do inputText com id “cep”. Logo em seguida já verificamos se foi digitado algum valor neste campo, caso contrário, a execução da consulta retorna imediatamente. Com os valores preenchidos fazemos chamada a uma URL passando o cep como parâmetro.

Se você acessar pelo navegador as seguintes URL's:

http://apps.widenet.com.br/busca-cep/api/cep.json?code=66070520 http://apps.widenet.com.br/busca-cep/api/cep.json?code=66070650

Perceba que o retorno será o seguinte, respectivamente:

{"status":1,"code":"66070-520","state":"PA","city":"Bel\u00e9m", "district":"Canudos","address":"Vila Soares"} {"status":1,"address":"Passagem Gracinha","district":"Terra Firme","city":"Bel\u00e9m","state":"PA","code":"66070-650"} Não passando nenhum parâmetro: http://apps.widenet.com.br/busca-cep/api/cep.json Você terá: {"status":0,"message":"CEP n\u00e3o informado []"}

Esse mesmo método get tem um result em JSON, onde, com esse objeto, primeiro verificamos seu status, podendo apenas ser valido quando for igual a 1, ou seja, qualquer valor diferente de 1 será considerado um erro.

O objeto “result” tem em sua estrutura os dados necessários que precisamos sobre as informações do endereço localizado naquele CEP: state, city, district e address.

Sendo assim, a consulta já foi realizada e os dados já foram capturados com sucesso e agora precisamos passar estas informações para o JSF. Tudo começa com a chamada do método logo no final da Listagem 4.

Para o JavaScript o método setarValoresCEP() é um método criado em algum arquivo JavaScript, mas veremos mais a frente que em nenhum momento criamos esse método explicitamente. Por agora, basta sabermos que podemos passar uma lista de valores no formato JSON, onde estes devem possuir o par “name-value”, que corresponde ao nome do parâmetro e o valor do parâmetro, respectivamente. Estes valores serão recuperados mais a frente no JSF.

Usando remoteCommand

Criaremos uma página index.xhtml que será onde o usuário digitará o valor do CEP a ser pesquisado, além de possuir o componente remoteCommand, como mostra a Listagem 5.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"> <h:head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="language" content="pt-br" /> <meta name="resource-types" content="document" /> <meta http-equiv="pragma" content="no-cache" /> <meta name="revisit-after" content="1" /> <meta name="classification" content="Internet" /> <meta name="robots" content="ALL" /> <meta name="distribution" content="Global" /> <meta name="rating" content="General" /> <meta name="doc-class" content="Completed" /> <meta name="doc-rights" content="Public" /> <meta name="url" content="" /> <title>LAB</title> <h:outputScript name="main.js" /> </h:head> <h:body> <h:form prependId="false"> <p:remoteCommand name="setarValoresCEP" process="@this" actionListener="#{javascriptMB.setarValoresCEP}" /> <h:panelGrid columns="2"> CEP: <p:inputText value="" id="cep" /> </h:panelGrid> <p:commandButton value="Consultar" onclick="consultaCep()" /> </h:form> </h:body> </html>
Listagem 5. index.xhtml

O início da página é referente a imports do JSF e do Primefaces, enquanto que entre as tags head são apenas para melhorar a indexação da nossa página em mecanismos de busca.

É importante salientar que estamos importando quatro libs:

xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"

Mas só usamos a h e p, então você pode até remover as linhas que representam as tags f e ui, pois não utilizamos.

O importante começa a ser notado na importação do main.js, que possui a consulta do CEP em JavaScript.

Dentro da tag temos o componente <p:remotecommand>. Vejamos em detalhes suas propriedades:</p:remotecommand>

Temos ainda um <p:commandbutton> que, ao ser clicado, chama o método consultaCEP() do main.js, e é exatamente neste ponto que tudo começa. Quando o método consultaCEP() é chamado, ele faz toda a consulta e depois chama o método setarValoresCEP() que é representado pelo remoteCommand, que por sua vez faz chamada ao ActionListener do ManagedBean.</p:commandbutton>

Temos quase todo projeto pronto, só falta definir o nosso ManagedBean JavascriptMB, presente na Listagem 6.

package br.com.lab; import java.util.Map; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import javax.faces.context.FacesContext; @ManagedBean(name = "javascriptMB") @ViewScoped public class JavascriptMB { public void setarValoresCEP(){ Map<String, String> requestParamMap = FacesContext.getCurrentInstance().getExternalContext() .getRequestParameterMap(); String cep = requestParamMap.get("cep"); String estado = requestParamMap.get("estado"); String cidade = requestParamMap.get("cidade"); String bairro = requestParamMap.get("bairro"); String endereco = requestParamMap.get("endereco"); System.out.println(“cep = “+cep); System.out.println(“estado = “+estado); System.out.println(“cidade = “+cidade); System.out.println(“bairro = “+bairro); System.out.println(“endereco = “+endereco); } }
Listagem 6. JavascriptMB

Optamos por deixar o ManagedBean o mais simples possível, pois o nosso foco é mostrar a comunicação do JavaScript com métodos no ManagedBean (JSF) e não detalhes referentes a persistência e etc. Nosso ManageBean está anotado com o @ViewScoped, assim ele irá manter seu estado enquanto o usuário estiver naquela determinada página, obviamente obedecendo um timeout.

Criamos um método/actionListener chamado setarValoresCEP() que, por coincidência, tem o mesmo nome do remoteCommand, mas podemos alterar para qualquer outro sem problema algum. O nosso actionListener recupera os parâmetros do contexto, que foram setados através do formato JSON na Listagem 4:

Map<String, String> requestParamMap = FacesContext.getCurrentInstance().getExternalContext() .getRequestParameterMap();

Capturamos os valores de cada parâmetro através do MAP e, apenas para efeito didático, estamos mostrando estes valores através de um System.out.println(), como vimos no final da Listagem 6.

Você poderá salvar esta consulta afim de posteriormente realizar auditorias ou cobranças em cima dessa estatística de consultas.

A gama de possibilidades que pode ser abrangida com o componente remoteCommand é enorme: o exemplo da consulta de CEP foi apenas uma delas. Em uma ponta você tem uma linguagem auxiliar (JavaScript) que é capaz de realizar tarefas de forma rápida e dar uma resposta quase em tempo real ao cliente, e do outro lado você tem uma linguagem robusta (Java) o suficiente para resolver lógicas e operações complexas de qualquer tipo de sistema.

Uma aplicabilidade para este componente pode ser a seguinte: Você pode desenhar uma figura com o canvas em JavaScript e ao passar o mouse sobre um ponto específico dessa figura você aciona um actionListener que fará uma consulta no banco de dados a procura dos dados daquele determinado ponto. Imagine o desenho de um campo de futebol onde, ao passar o mouse sobre um ponto específico, o sistema consiga buscar qual jogador está alocado naquele determinado ponto. Inúmeras são as aplicações. O primeiro passou foi dado nesse artigo.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados