Conhecendo AJAX
Turbine a Interatividade de suas Aplicações Web. O uso de Ajax pode deixar suas aplicações web mais ágeis e responsivas, e é a base da interface gráfica de software de grande sucesso com o Gmail.
Afinal, o que é AJAX?
Para entender o AJAX, é interessante recapitular o funcionamento de requisições / respostas e a exibição de paginas web, e soluções para problemas comuns que ocorrem com essas operações. Numa paginas web, quando o usuário clica num link ou submete um formulário,o servidor processa as informações submetidas e devolve como resposta outra página web completa, que então é renderizada pelo browser em substituição à primeira. Um problema é que o atraso da transferência dos dados, somando ao tempo de renderização pelo browser resulta na apresentação de páginas vazias e lentidão na resposta a ações do usuário.
Para minimizar esse efeito, uma abordagem é fazer a submissão de parâmetros e dados por um “canal separado” (através de uma thread própria dentro do browser) utilizando JavaScript. Em seguida o servidor devolve a resposta através do mesmo canal, evitando a necessidade de uma nova renderização da página. Dessa forma, a resposta não terá o conteúdo de uma página inteira, mas apenas os dados que necessitam ser modificados (ex.: o conteúdo de uma combobox). Um trecho de código JavaScript se encarrega de manipular esses dados e atualizar a página – e é dada a impressão para o usuário de uma aplicação mais interativa e responsiva.
Tal idéia existe desde que se começou a usar scripts para manipular elementos de páginas HTML. Inicialmente, a técnica do “canal separado” era implementada por meio de um frame HTML de tamanho mínimo, que realizava a requisição e obtinha como resposta uma pagina contendo, além dos dados, um script que atualizava o frame principal. O problema é que tal abordagem aumenta demais a complexidade de gerenciamento. Construir uma página desse modo exige uma página contendo o frameset (que define o layout dos frames na janela), um frame principal para mostrar os dados, e uma ou mais páginas para implementar a atualização do frame principal (cada atualização normalmente precisa de uma página de resposta específica).
A situação melhorou com a introdução, no Internet Explorer 5, da classe XMLHttpRequest, que permite a requisição assíncrona de páginas web e a manipulação do conteúdo dessas páginas na forma de dados (desde que a página esteja num formato apropriado). Isso simplificou muito a implementação de requisições através de threads próprias dentro do browser, sem a necessidade de utilização de utilização de artifícios como frames escondidos.
Além disso, num esforço por maior compatibilidade entre browser, o grupo Mozilla implementou uma classe equivalente ao XMLHttpRequest, o que foi seguido por outros browsers como Safari, Konqueror, OmniWeb e Opera. Criou-se então uma ambiente propício para atualização da classe XMLHttpRequest (ou equivalentes) por grandes aplicações como GMail, Google Earth, Google Suggest e Yahoo! Flickr, entre outras.
A técnica de criar uma página atualizada dinamicamente e parcialmente através de requisições ao servidor, utilizando o objeto XMLHttpRequest, ficou conhecida como AJAX.
Implementação AJAX
Após esse posicionamento histórico e tecnológico, está na hora de vermos a técnica em ação. Como exemplo, vamos criar uma página simples, onde temos uma combobox com projetos cadastrados num sistema: ao mudar o projeto selecionado, é mostrada logo abaixo da combo uma tabela com as atividades cadastradas naquele projeto.
A Listagem 1 apresenta a página de exemplo. A chamada a Fabrica.getProjetos() simula a obtenção de todos os projetos cadastrados no sistema; sua implementação não é discutida neste artigo, porém os fontes completos podem ser obtidos no site da Java Magazine.
O fragmento JavaScript abaixo cria a referência para o objeto XMLHttpRequest:
var xmlhttp=false;
try {
xmlhttp=new ActiveXObject(“Msxml2.XMLHTTP”);
} catch (e) {
try { xmlhttp=new ActiveXObject(“Microsoft.XMLHTTP”);}
catch (E) { xmlhttp=false;}
}
if (!xmlhttp && typeof XMLHttpRequest!=’undefined’){
xmlhttp=new XMLHttpRequest();
}
Este script é encontrado como exemplo em vários sites sobre AJAX e tem sido considerado um dos padrões para a criação da instância de XMLHttpRequest de forma independente de browser. Inicialmente, tenta-se obter a instância de Msxml12.XMLHTTP ou Microsoft.XMLHTTP, para o Internet Explorer (o nome da classe depende da versão da biblioteca de XML da Microsoft instalada na máquina cliente). Caso o browser tenha a sua própria implementação do tipo XMLHttpRequest, o script cria uma instância desse tipo no bloco condicional no final do script.
O evento onchange da combobox invoca a função JavaScript alteraProjeto():
function alteraProjeto() {
var theUrl=”obtemAtividadesXML.jsp?projeto=”
+document.getElementByld(“projeto”).value;
xmlhttp.open(“GET,theUrl,true);
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4) {
criaTabela(xmlhttp.responseXML);
}
}
xmlhttp.send(null);
};
Essa função requisita a página obtemAtividadesXML.jsp, passando como parâmetros a identificação do projeto selecionado. Note que a execução de uma chamada AJAX segue sempre os mesmos passos:
- É invocado o método open() do objeto XMLHttpRequest, passando o método http desejado (normalmente GET ou POST), além da URL a ser requisitada e um flag indicando se a chamada é assíncrona ou não.
- Como a chamada é assíncrona, é definido no nosso caso um manipulador de eventos para notificar quando a resposta do servidor estiver completa (readyState igual a 4). Será invocada a função criaTabela(), passando-se o conteúdo XML obtido como resposta do servidor.
- Finalmente, o método send() realiza o envio da requisição, passando como parâmetro o corpo da mensagem a ser enviado (no caso de uma requisição POST).
A página obtemAtividadesXML.jsp, apresentada na Listagem 2, é responsável por gerar um documento XML com os dados de atividades do projeto passado como parâmetro. A Listagem 3 apresenta o resultado da execução da página JSP: esse XML será utilizado para facilitar a explicação do código JavaScript que faz a criação da tabela.
A página JSP utiliza somente conceitos de JSP e JSTL. O único detalhe que vale a pena mencionar é a inclusão da tag <pag> definindo o tipo de conteúdo da resposta (contentType) como sendo text/xml. Isso é importante para que o objeto XMLHttpRequest no browser consiga montar as estrutura XML a partir desse conteúdo.
Um ponto muito importante é que a manipulação dos dados de resposta como uma estrutura XML dentro do JavaScript requer duas condições:- O content-type de página de resposta deve ser definido como text/xml.
- Os dados de resposta precisam ser obtidos através da propriedade responseXML do objeto XMLHttpRequest.
Finalmente, a função JavaScript criaTabela() monta uma tabela HTML utilizando os dados XML obtidos pelo objeto XMLHttpRequest, e define essa tabela como valor do campo innerHTML do elemento Dentro dessa função, toda a manipulação dos dados do XML é feita através do método getElementByTagName() do objeto XMLDocument. Esse método recebe como parâmetro um nome da tag e retorna uma lista de elementos XML referentes à tag passada. Cada elemento XML será representado pela sua estrutura DOM. Assim, a linha: Atribui à variável data um array com três elementos (considerando como resposta o XML da Listagem 3). Cada elemento representa a estrutura DOM de cada atividade definida dentro da tag raiz <dados>.
Com isso, definimos os conceitos básicos para o próximo comando, que é responsável por obter o valor das tags < descricao>, <dataInicial> e <dataFinal>, dentro de cada tag <atividade>: Como já visto, getElementsByTagName() retorna um array com as estruturas DOM representando os elementos associados à tag passada. Neste caso, o método retorna uma lista de apenas um elemento, pois só existe uma tag < descricao> dentro de < atividade>. Como o retorno é uma lista e sabemos que haverá um só elemento, usamos diretamente uma chamada item(0). Dessa forma, a variavel desc contém a estrutura DOM da tag < descricao> Alguma confusão às vezes acontece quando é necessário obter o texto que está dentro de uma tag, mas não se está acostumado com a estrutura DOM. No exemplo, o texto da tag <descricao> é obtido com o seguinte comando: Pode parecer estranha a construção firstChild.data: como firstChild denota a primeira tag filha de uma tag, e <descricao> não possui tags filhas, então porque fazer o acesso dessa forma? A questão é que em DOM, o texto dentro de uma tag na realidade é o valor de uma tag filha especial. Assim, para obter a string “texto” dentro de <descricao>, é necessário primeiro obter essa tag filha (através da propriedade firstChild) para então obter o texto a partir da sua propriedade data. Pessoalmente, acho a manipulação da resposta do servidor através do modelo DOM pouco prática, além de não ser reutilizável – principalmente quando é necessário manipular estruturas de respostas mais complexas, com vários níveis de tags aninhadas. Melhor seria se pudéssemos manipular diretamente objetos JavaScript, o que facilitaria a criação de rotinas AJAX reutilizáveis, que pudessem ficar definidas num arquivo de inclusão separado, ou mesmo ser encapsuladas em taglibs JSP. Para isso, podemos utilizar a notação JSON (JavaScript Object Notation) em vez de XML, para a formatação dos dados nas páginas de resposta. A vantagem é que o JavaScript consegue, a partir de um documento escrito em JSON, criar automaticamente a estrutura de objetos correspondente, o que não seria possível para uma estrutura XML. Você pode ver uma definição mais formal de JSON no links, mas por ora, o que é necessário saber que é essa notação representa uma estrutura de objetos através da definição de pares atributo-valor na seguinte forma: Um array de objetos escritos em JSON é uma seqüência de definições de objetos, como a mostrada acima, separadas por vírgulas e delimitadas por colchetes: Para aplicar estruturas JSON no nosso exemplo, primeiro alteramos a página de resposta para que as informações sobre as atividades associadas a um projeto sejam escritas nessa notação e não em XML. A alteração pode ser vista na Listagem 4. Note que não é mais necessário definir o conteúdo da resposta como text/xml. Na realidade, não fará diferença, ao usar JSON, qual o tipo de conteúdo de retorno, desde que seja do grupo text/xxxx. A Listagem 5 apresenta a página de lista de atividades, alterada para o suporte a JSON. As diferenças mais significativas são: Note como se tem maior simplicidade e legibilidade com o modo de acesso em JSON. Cada elemento do array data é um objeto JavaScript com as propriedades descricao, dataInicial e dataFinal, as quais podem ser acessadas diretamente através do operador “ponto”. Enquanto que em uma estrutura XML, sempre seria necessário navegar dentro da hierarquia DOM para obter os dados desejados. AJAX é uma técnica que, de uma forma ou de outra, vem sendo aplicada em aplicações web há algum tempo. Recordo-me de um projeto em 2001 que utilizava um conceito semelhante, mas lançando mão de frames escondidos para realizar a tarefa de requisição de dados e atualização da página principal. Também tenho noticia de vários projetos que já faziam uso da atualização dinâmica de páginas através do objeto XMLHttpRequest, nessa mesma época. Então podemos perguntar o porquê de tanto alarde e de um novo nome para uma técnica antiga. Existem várias discussões na internet sobre o tema, algumas atacando a empresa Adaptive Path por tentar assumir o crédito (veja links), outras argumentando que o AJAX não passa de oportunismo de alguns aproveitando a onda de interfaces internet “ricas” (RIAs ou Rich Internet Applications) e outros defendendo uma miríade de opiniões (contra e a favor) sobre o AJAX. De uma perspectiva pragmática, minha opinião sobre AJAX é que se trata de uma técnica antiga que está sendo resgatada e destacada, principalmente porque o problema da incompatibilidade entre browser que existia no começo da década foi hoje em grande parte resolvido. O fato é que hoje em grande parte resolvido. O fato é que AJAX traz um benefício muito grande para as interfaces de aplicações web, com custo relativamente baixo. E levando em consideração o número de bibliotecas e tag libraries que estão surgindo com o objetivo de facilitar ainda mais a criação de interfaces AJAX, esta técnica deveria ser carta sempre disponível na manga do desenvolvedor web. Finalmente, sempre que surge uma “grande nova onda” é importante observar o movimento dos grandes da indústria, em vez de ouvir os neo-xiitas tecnológicos de plantão: Em resumo, nova ou não, a técnica AJAX apresenta uma visão diferente para o desenvolvimento de interfaces mais intuitivas e usáveis para o ambiente web, sem a necessidade de applets ou plug-ins. Desenvolvedor com AJAX “no braço” já não é mais uma tarefa complicada, e com os novos frameworks e taglibs que estão surgindo, a tarefa irá ficar mais simples ainda. Então, para aqueles que (como eu) sempre consideraram JavaScript um “mal necessário”, fica a sugestão: talvez seja a hora de tirar a poeira daquele manual de JavaScript e começar a olhar a arquitetura de sistemas web de uma perspectiva mais interativa!
var data = AjaxResponse.getElementByTagName(“atividade”);
var desc=data[i];getElementsByTagName(“descricao”).item(0);
desc.firstChild.data
<%@ page import=”jm.ajax.data.*”%>
<%@ taglib uri=http://java.sun.com/jsp/jstl/core” prefix=”c”%>
<script type=”text/javascript”>
var xmlhttp=false;
try {
xmlhttp = new ActiveXObject(“Msxml2.XMLHTTP”);
} catch (e) {
try { xmlhttp = new ActiveXObject(“Microsoft.XMLHTTP”); }
catch (E) { xmlhttp = false; }
}
if (!xmlhttp && typeof XMLHttpRequest!=’undefined’) {
xmlhttp = new XMLHttpRequest();
}
function alteraProjeto()
{
var theUrl = “obtemAtividadesXML.jsp?projeto=”
+ document.getElementById(“projeto”).value;
xmlhttp.open(“GET”, theUrl,true);
xmlhttp.onreadystatechange-function() {
if (xmlhttp.readyState==4) {
criaTabela(xmlhttp.responseXML);
}
}
xmlhttp.send(null);
};
function criaTabela(AjaxResponse)
{
var data = AjaxResponse.getElementsByTagName(“atividade”);
var htmlText = “<table border=’1’>”
+” <tr>”
+” <th>Descrição</th>”
+” <th>Data Inicial</th>”
+” <th>Data Final</th>”
+” </tr>”
for(i=0;i<data.length;i++)
{
var desc = data[i].getElementsByTagName(
“descrição”).item(0);
var dataInicial = data[i].getElementsByTagName(
“dataInicial”).item(0);
var dataFinal = data[i].getElementsByTagName(
“dataFinal”).item(0);
htmlText=htmlText+”<tr><td>”
+ desc.firstChild.data+”</td><td>”
+ dataInicial.firstChild.data+”</td><td>”
+ dataFinal.firstChild.data+”</td><td></tr>”;
}
htmlText=htmlText+”</table>”;
document.getElementById(“tabela”).innerHTML=htmlText;
}
</script>
<html>
<c:set var=”projetos” value=”<%= Fabrica.getProjetos()%>” />
<center>
<select id=”projeto” onchange=”javascript:alteraProjetos()”>
<c:forEach var=”p” items=”$”>
<option value=”${p.id}”>${p.nome}</option>
</c:forEach>
</select>
<br><br>
<div id=”tabela”></div>
</center>
</html>
<%@ page import=”jm.ajax.data.*” %>
<%@ page contentType=”text/xml” %>
<%@ taglib uri=http://java.sun.com/jsp/jstl/core” prefix=”c” %>
<%@ taglib uri=http://java.sun.com/jsp/jstl/fmt” prefix=”fmt” %>
<c:set var=”atividades” value=
“<%= Fabrica.getAtividades(request.getParameter(“projeto”))%>” />
<dados>
<c:forEach var=”atividade” items=”$”
varStatus=”status”>
< atividade id=”${atividade.id}”>
<descricao>${atividade.descricao}</descricao>
<dataInicial>
<fmt:formatDate value=”${atividade.dataInicial}”
dataStyle=”short”/>
</dataInicial>
<dataFinal>
<fmt:formatDate value=”${atividade.dataFinal}”
dateStyle=”short”/>
</dataFinal>
< /atividade>
</c:forEach>
</dados>
<?xml version=”1.0” encoding=”UTF -8”?>
<dados>
< atividade id=”1”>
<descricao>Instalar Laszlo</descricao>
<dataInicial>01/08/05</dataInicial>
<dataFinal>01/08/05</dataFinal>
< /atividade>
< atividade id=”2”>
<descricao>Criar o projeto exemplo</descricao>
<dataInicial>02/08/05</dataInicial>
<dataFinal>08/08/05</dataFinal>
< /atividade>
< atividade id=”3”>
<descricao>Escrever o artigo</descricao>
<dataInicial>10/08/05</dataInicial>
<dataFinal>20/08/05</dataFinal>
< /atividade>
</dados>
Implementando AJAX com JSON
{atr1:vlr1,atr2: vlr2, ..., atrN, vlrN}
[{atr1.1:vlr1.1,...,atr1.N,vlr1.N}, {atr2.1,vlr2.1,...,atr2.N:vlr2.N},...]
data[i].getElementsByTagName(
“dataInicial”).item(0).firstChild.data
<%@ page import=”jm.ajax.data.*” %>
<%@ page contentType=”text/xml” %>
<%@ taglib uri=http://java.sun.com/jsp/jstl/core” prefix=”c” %>
<%@ taglib uri=http://java.sun.com/jsp/jstl/fmt” prefix=”fmt” %>
<c:set var=”atividades” value=
“<%= Fabrica.getAtividades(request.getParameter(“projeto”))%>” />
[
<c:forEach var=”atividade” items=”$” varStatus=”status”>
{
id:’${atividade.id}’,
descricao:’${atividade.descricao}’,
dataInicial: ‘<fmt:formatDate value=
“${atividade.dataInicial}” dateStyle=”short”/>’,
dataFinal: ’<fmt:formatDate value=
“${atividade.dataFinal}” dateStyle=”short”/>’
}
<c:if test=”${not status.last}”>
</c:if>
</c:forEach>
]
<%@ page import=”jm.ajax.data.*” %>
<%@ taglib uri=http://java.sun.com/jsp/jstl/core” prefix=”c” %>
<script>
var xmlhttp=false;
try {
xmlhttp = new ActiveXObject(“Msxml.XMLHTTP”);
}catch (e) {
try { xmlhttp = new ActiveXObject(“Microsoft.XMLHTTP”);}
catch(E) { xmlhttp = false; }
}
If (!xmlhttp && typeof XMLHttpRequest!=’undefined’) {
Xmlhttp = new XMLHttpRequest();
}
function alteraProjeto()
{
var theUrl = “obtemAtividades.jsp?projeto=”
+ document.getElementById(“projeto”).value;
xmlhttp.open(“GET”, theUrl.true);
xmlhttp.onreadystatechange=function() {
If (xmlhttp.readyState==4) {
criaTabela(xmlhttp.responseText);
}
}
xmlhttp.send(null);
};
function criaTabela(AjaxResponse)
{
var data = eval(AjaxResponse);
var htmlText = “<table border=’1’>”+
“ <tr>”+
“ <th> Descrição</th>”+
“ <th>Data Inicial</th>”+
“ <th>Data Final</th>”+
“ </tr>”
for(i=0;i<data.length;i++)
{
htmlText=htmlText+”<tr><td>”
+ desc.firstChild.data+”</td><td>”
+ dataInicial.firstChild.data+”</td><td>”
+ dataFinal.firstChild.data+”</td><td></tr>”
}
htmlText=htmlText+”</table>”;
document.getElementById(“tabela”).innerHTML=htmlText;
}
</script>
<html>
<c:set var=”projetos” value=”<%= Fabrica.getProjetos()%>” />
<center>
<select id=”projeto” onchange=”javascript:alteraProjetos()”>
<c:forEach var=”p” items=”$”>
<option value=”${p.id}”>${p.nome}</option>
</c:forEach>
</select>
<br><br>
<div id=”tabela”></div>
</center>
</html>
Conclusões
Confira também
Artigos relacionados