ASP.NET Turbinado: AJAX

Por Karl Seguin

Setembro de 2005

Aplica-se a:

  • AJAX (JavaScript e XML assíncronos)

  • Microsoft AJAX.NET

  • Microsoft ASP.NET

Resumo: Saiba como o AJAX (JavaScript e XML assíncronos) pode ser usado para tornar seus aplicativos Microsoft ASP.NET mais dinâmicos e com maior capacidade de resposta. Este artigo também contém links para páginas em inglês. (17 páginas impressas).

Baixe o código de exemplo deste artigo, AjaxASPNETCS.msi em C#

Baixe o código de exemplo deste artigo, AjaxASPNETVB.msi em Visual Basic

Nesta página

Introdução
O que é o AJAX?
AJAX para ASP.NET
AJAX prático
AJAX e você
Conclusão

Introdução

Desde que a programação da Web foi iniciada, sempre houve muito intercâmbio entre os aplicativos da Web e aplicativos de desktops. Por exemplo, é geralmente aceito que os aplicativos da Web não fornecem interface de usuário tão esmerada como a dos aplicativos de desktop. Por outro lado, os aplicativos da Web são independentes de plataforma e oferecem um mecanismo de desenvolvimento mais fácil. Uma área que tem sido uma batalha constante para os desenvolvedores da Web é a tarefa aparentemente simples de fornecer aplicativos com mais capacidade de resposta.

Tradicionalmente, uma resposta para uma entrada de usuário só podia ser recuperada enviando-se uma nova solicitação ao servidor Web. Em alguns casos, os desenvolvedores podiam carregar todas as respostas no cliente (usando JavaScript) e proporcionar uma melhor experiência ao usuário. Um exemplo comum dessa técnica é o carregamento dinâmico de uma lista de estados ou províncias com base em um país selecionado. Infelizmente, em muitos casos, nem a postagem nem o carregamento de tudo em JavaScript parecia correto. Ou a postagem criava desconexões de UI excessivas ou era requerido do cliente uma quantidade de dados difícil de ser gerenciada (o que freqüentemente resultava em JavaScript ilegível). O AJAX oferece uma nova alternativa intermediária, capaz de aproveitar o aplicativo baseado em servidor e mantendo, simultaneamente, uma sensação de agilidade e capacidade de resposta.

O que é o AJAX?

AJAX, a abreviação de Asynchronous JavaScript And XML, não é uma tecnologia mas um agrupamento de tecnologias. O AJAX usa uma tecnologia de comunicação (geralmente SOAP e XML) para enviar para o servidor e receber dele solicitações/respostas assíncronas, e aproveita tecnologias de apresentação (JavaScript, DOM, HTML e CSS) para processar a resposta. É razoável que os aplicativos usem o AJAX atualmente, porque a maioria dos navegadores dá suporte à tecnologia necessária. Para obter uma definição mais detalhada do AJAX, visite a entrada de AJAX na Wikipedia.

O que AJAX realmente significa? Ele permite executar um método do lado do servidor por meio de uma chamada de JavaScript, sem que você precise atualizar o navegador. Imagine-o como uma mini-solicitação/resposta que acontece nos bastidores para o usuário. Se para você ainda não está claro o que é o AJAX, examine dois exemplos populares do Google: Google Suggests e Google Maps. Se você for iniciante no AJAX, ficará arrepiado com a capacidade de resposta desses dois aplicativos.

AJAX para ASP.NET

Existem muitos detalhes técnicos no AJAX para que ele funcione. É bem possível que você não queira gastar horas ou dias decifrando o que há dentro do AJAX, mas prefira começar hoje mesmo a criar aplicativos habilitados para AJAX, para atender às demandas existentes (e se desejar saber como ele funciona internamente, certamente não é a mim que você deve perguntar). Existem várias ferramentas que os desenvolvedores podem usar para começar rapidamente. Especificamente, examinaremos a ferramenta Ajax.NET, gratuita e de código aberto, desenvolvida por Michael Schwarz. A Ajax.NET cuida de todos os detalhes da implementação, é compatível com .NET e pode ser estendida. O Microsoft ASP.NET 2.0 introduz seu próprio tempero com retornos de chamada assíncronos por meio do recurso Client Callback, e foi anunciado recentemente que uma implementação do AJAX, com o codinome "Atlas", está a caminho.

A terminologia pode ser um pouco confusa, mas quando falo de AJAX, estou me referindo à estrutura geral das funções de chamadas assíncronas do lado do servidor a partir do cliente. Quando menciono Ajax.NET, estou me referindo a uma implementação específica que ajuda a criar soluções que aproveitam a estrutura do AJAX.

Para saber mais sobre o recurso Client Callback do ASP.NET 2.0, visite o blog de Bertrand Le Roy.

AJAX prático

O restante deste artigo será devotado principalmente a três exemplos significativos que utilizam o poder do AJAX usando a ferramenta Ajax.NET. O guia contém código em Microsoft C# e Microsoft Visual Basic .NET, algumas vezes ambos são fornecidos, outras, apenas um deles. O código que faz tudo funcionar é tão fácil, que os desenvolvedores de C# não terão dificuldades em seguir o código somente para o Visual Basic .NET e vice-versa! Foram incluídos neste artigo projetos de exemplo do C# e Visual Basic .NET para download, e eles fornecem código de funcionamento e execução. Antes de chegarmos aos exemplos, é necessário instruções elementares sobre como configurar a Ajax.NET e trabalhar com ela.

Ajax.NET

A documentação e o site da AJAX.NET ajudam os desenvolvedores a começar a trabalhar. Falaremos rapidamente sobre as principais etapas que você precisa conhecer antes de examinar alguns exemplos concretos dessa nova tecnologia em uso.

Comece baixando e descompactando o arquivo do AJAX no site de projeto do AJAX.NET, criando um novo projeto ASP.NET (em Visual Basic .NET ou C#, conforme você preferir) e adicionando uma referência ao arquivo AJAX.dll. Além dessa, a única outra etapa de configuração será adicionar este código ao arquivo web.config, dentro do elemento <system.web>.

<configuration>    
 <system.web>  
  <httpHandlers>
   <!-- Register the ajax handler -->
   <add verb="POST,GET" path="ajax/*.ashx" 
        type="Ajax.PageHandlerFactory, Ajax" />
  </httpHandlers>  
  ...
  ... 
 </system.web>
</configuration>

Para tornar as funções do lado do servidor disponíveis através de JavaScript, é preciso executar dois procedimentos. Em primeiro lugar, a função ou as funções em questão precisam ser marcadas com o atributo Ajax.AjaxMethodAttribute. Em segundo lugar, a classe que contém essas funções precisa ser registrada por meio de uma chamada ao Ajax.Utility.RegisterTypeForAjax durante o evento de carregamento da página. Isso pode parecer complicado, mas não se preocupe, são apenas duas linhas extras no código. Vamos examinar um exemplo.

//C#

public class Sample : System.Web.UI.Page
{
 private void Page_Load(object sender, System.EventArgs e)
 {
  //Register the class containing the server-side function
  //we are interested in
  Ajax.Utility.RegisterTypeForAjax(typeof(Sample));
 }
 [Ajax.AjaxMethod()]
 public string GetMessageOfTheDay()
 {
  return "Experience is the mother of wisdom";
 }
}
'VB.NET
Public Class Sample
 Inherits System.Web.UI.Page

 Private Sub Page_Load(sender AsObject, e As EventArgs) 
                                         Handles MyBase.Load
  'Register the class containing the server-side function
  'we are interested in
  Ajax.Utility.RegisterTypeForAjax(GetType(Sample))
 End Sub
 <Ajax.AjaxMethod()> _
 Public Function GetMessageOfTheDay() As String
  Return "Experience is the mother of wisdom"
 End Function
End Class

O exemplo acima informa à Ajax.NET para examinar a classe Sample de métodos amigáveis para Ajax. Acontece que essa é a mesma classe da página atual, mas poderia ser qualquer classe .NET ou várias classes poderiam ser registradas. A Ajax.NET examinará a classe específica de quaisquer métodos marcados com AjaxMethodAttribute, das quais a classe Sample possui um, GetMessageOfTheDay.

Isso feito, resta apenas usá-lo em JavaScript. A Ajax.NET cria automaticamente uma variável JavaScript com o nome igual ao da classe registrada (neste exemplo, será Sample) que expõe as funções cujos nomes são iguais ao do AjaxMethod (neste exemplo, GetMessageOfTheDay). Veja sua aparência.

<script language="javascript">
 Sample.GetMessageOfTheDay(GetMessageOfTheDay_CallBack);
 function GetMessageOfTheDay_CallBack(response)
 {
  alert(response.value);
 }
</script>

A função GetMessageOfTheDay de JavaScript espera os mesmos parâmetros de sua contraparte do lado do servidor (neste caso, nenhum) além da função de retorno de chamada de JavaScript para executar e transmitir a resposta quando esta estiver pronta. Vemos aqui o funcionamento da natureza assíncrona de AJAX, uma vez que a chamada para GetMessageOfTheDay não impede a execução de outro código JavaScript, nem impede que o usuário continue a trabalhar na página. Quando o processamento no lado do servidor é concluído, a Ajax.NET chama a função de retorno de chamada especificada, GetMessageOfTheDay_CallBack, e transmite a ela a resposta que abrange o valor de retorno do lado do servidor.

O mapeamento entre o código do lado do servidor e o código JavaScript pode ser confuso. A Figura 1 mostra a estrutura do código do lado do servidor e também do código JavaScript, juntamente com o mapeamento entre os dois.

Aa479042.ajaxspiced_fig01(pt-br,MSDN.10).gif
Figura 1. Mapeamento entre o código do lado do servidor e o código JavaScript

Seguramente, existem mais detalhes interessantes na Ajax.NET, como o suporte para tipos .NET e a riqueza das respostas de retorno de chamada (mais que apenas um valor). Esperamos que os exemplos a seguir destaquem alguns dos recursos e ajudem você a imaginar como o AJAX pode ajudá-lo a criar um aplicativo bem-sucedido.

Exemplo 1: Lista suspensa vinculada

Discutimos rapidamente no início deste artigo as duas abordagens tradicionais usadas para vincular uma lista DropDownList a outra. A página é postada de volta quando o índice selecionado é alterado ou todos os dados possíveis são carregados em matrizes JavaScript e exibidos dinamicamente. Esperamos que você possa perceber como é possível usar o AJAX como uma alternativa para essas duas soluções.

Primeiro, vamos examinar nossa interface de dados e conduzir o exemplo a partir desse ponto. Nossa camada de acesso a dados exporá dois métodos: o primeiro recupera uma lista de países aos quais o sistema dá suporte enquanto o segundo obtém uma identificação de país e retorna uma lista de estados/províncias. Como isso é puro acesso a dados, vamos examinar as assinaturas dos métodos.

//C#
public static DataTable GetShippingCountries();
public static DataView GetCountryStates(int countryId);
'VB.NET
Public Shared Function GetShippingCountries() As DataTable
Public Shared Function GetCountryStates(ByVal countryId As Integer)
                                                        As DataView

Agora, vamos para a camada oposta para criar nosso formulário da Web simples.

<asp:DropDownList ID="countries" Runat="server" /> 
<asp:DropDownList ID="states" Runat="server" />
<asp:Button ID="submit" Runat="server" Text="Submit" />

O evento Page_Load é igualmente simples e tão comum quanto o formulário da Web anterior. Usamos nossa camada de acesso a dados para recuperar os países disponíveis e ligá-los à nossa lista countries DropDownList.

//C#
if (!Page.IsPostBack)
{
 countries.DataSource = DAL.GetShippingCountries();
 countries.DataTextField = "Country";
 countries.DataValueField = "Id";
 countries.DataBind();
 countries.Items.Insert(0, new ListItem("Please Select", "0"));
}

É aqui que nosso código deixa de ser típico. Em primeiro lugar, vamos criar nossa função do lado do servidor a ser chamado do JavaScript.

'VB.NET
<Ajax.AjaxMethod()> _
Public Function GetStates (ByVal countryId As Integer) As DataView
 Return DAL.GetCountryStates(countryId)
End Function

Isso parece como qualquer outra função normal: espera a identificação do país desejado e transmite a solicitação para o DAL. A única diferença é que marcamos o método com o atributo AjaxMethodAttribute. A última etapa a ser executada do lado do servidor é registrar nossa classe que contém o método acima (neste caso, nosso code-behind) na Ajax.NET através de uma chamada para RegisterTypeForAjax.

//C#
Ajax.Utility.RegisterTypeForAjax(typeof(Sample));
'VB.NET
Ajax.Utility.RegisterTypeForAjax(GetType(Sample))

Está quase terminado; resta apenas chamar o método GetStates de JavaScript que trata da resposta. Logicamente, desejamos chamar GetStates quando o usuário selecionar um novo item na lista de países. Para isso, vamos nos conectar ao evento onChange do JavaScript. O código do formulário da Web é ligeiramente alterado.

<asp:DropDownList onChange="LoadStates(this)" 
                  ID="countries" Runat="server" />

A função LoadStates de JavaScript será responsável pela emissão da solicitação assíncrona por meio do proxy criado por Ajax.NET. Lembre-se que, por padrão, o proxy criado por Ajax.NET tem o formato <NomeDoTipoRegistrado>.<NomeDoMétodoDoLadoDoServidor>. No nosso caso, isso será Sample.GetStates. Também vamos querer transmitir nosso parâmetro de identificação de país e a função de retorno de chamada a ser chamada por Ajax.NET quando a função do lado do servidor estiver concluída.

//JavaScript
function LoadStates(countries)
{
 var countryId = countries.options[countries.selectedIndex].value;
 Sample.GetStates(countryId, LoadStates_CallBack);
}

Por fim, a última etapa é tratar da resposta na função LoadStates_CallBack. É provável que o recurso mais produtivo da Ajax.NET seja seu suporte a diversos tipos .NET (isso já foi mencionado algumas vezes). Lembre-se que a função do lado do servidor retornou um DataView. O que JavaScript conhece de DataViews? Nada, mas JavaScript é uma linguagem orientada a objetos e a Ajax.NET não cuida apenas de criar um objeto semelhante ao DataView do .NET, ela também mapeia o valor de retorno da função para o clone de JavaScript. Tenha em mente que o DataView de JavaScript é simplesmente uma réplica do DataView real e que (atualmente) ele não dá suporte a muito mais funcionalidade do que fazer loop nas linhas e acessar os valores das colunas (funcionalidade como a definição de propriedades RowFilter ou Sort).

function LoadStates_CallBack(response)
{
 //if the server-side code threw an exception
 if (response.error != null)
 {
  //we should probably do better than this
  alert(response.error); 
  return;
 }

 var states = response.value;  
 //if the response wasn't what we expected  
 if (states == null || typeof(states) != "object")
 {
  return;
 }
 //Get the states drop down
 var statesList = document.getElementById("states");
 statesList.options.length = 0; //reset the states dropdown

 //Remember, its length not Length in JavaScript
 for (var i = 0; i < states.length; ++i)
 {
  //the columns of our rows are exposed like named properties
  statesList.options[statesList.options.length] = 
         new Option(states[i].State, states[i].Id);
 }
}

Depois de uma pequena verificação de erros, o JavaScript anterior obtém a lista suspensa de estados, faz loop no valor da resposta e adiciona opções à lista suspensa dinamicamente. O código é limpo, simples e estranhamente semelhante a C# e Visual Basic .NET. Pessoalmente, como um desenvolvedor que criou e vinculou matrizes de JavaScript baseadas em variáveis do lado do servidor, ainda tenho dificuldades em acreditar que isso realmente funciona.

Existe uma questão importante que talvez não seja óbvia. Como a DropDownList foi criada dinamicamente em JavaScript, seus itens não fazem parte de ViewState e não serão mantidos. Isso significa que o manipulador de eventos OnClick do nosso botão precisará executar algum trabalho extra.

'VB.NET
Private Sub submit_Click(sender As Object, e As EventArgs)
  Dim selectedStateId As String = Request.Form(states.UniqueID)

  'should do some user validation...
  states.DataSource = 
     DAL.GetCountryStates(Convert.ToInt32(countries.SelectedIndex))
  states.DataTextField = "State"
  states.DataValueField = "Id"
  states.DataBind()
  states.SelectedIndex = 
    states.Items.IndexOf(states.Items.FindByValue(selectedStateId))
End Sub

Primeiro, não podemos usar a propriedade states.SelectedValue e precisamos usar Request.Form. Segundo, se desejarmos exibir novamente a lista para o usuário, precisaremos ligar a DropDownList dos estados, felizmente reutilizando o mesmo método de acesso a dados. Finalmente, o valor selecionado precisa ser definido através de programação.

Exemplo 2: Bloqueador de documentos

Para o nosso próximo exemplo, usaremos um recurso mais completo que será aprimorado com AJAX. Esse exemplo será um sistema simples de gerenciamento de documentos. Como qualquer sistema de gerenciamento de documentos decente, precisamos oferecer gerenciamento de concorrência. Isto é, precisamos ter um meio para lidar com dois usuários que tentem editar o mesmo documento. Faremos isso criando algum tipo de mecanismo de bloqueio que impeça um usuário de editar um documento que já esteja sendo editado. Vamos usar o AJAX para tornar mais agradável a experiência do usuário com o mecanismo de bloqueio. Em primeiro lugar, vamos criar uma fila de documentos que o usuário tentou editar mas não conseguiu (porque já estavam sendo editados) e notificá-lo automaticamente quando os documentos estiverem disponíveis. Em segundo lugar, asseguraremos que um documento será desbloqueado se o usuário fechar seu navegador ou sair do documento. Esse último recurso ajuda a garantir que os documentos não fiquem bloqueados para sempre. Para a finalidade deste guia, a funcionalidade não relacionada especificamente à implementação de AJAX será ignorada; entretanto, o projeto para download contém tudo.

Em primeiro lugar, tentaremos obter um bloqueio exclusivo para o documento quando um usuário tentar editá-lo e, se não conseguirmos, adicionaremos o documento à fila do usuário, e este será encaminhado de volta para a página principal. Não há nada específico de AJAX aqui, mas vamos examinar o código para fornecer o contexto necessário ao exemplo. No evento OnLoad da Page usada para edição, é incluído o seguinte:

//C#
if (!Page.IsPostBack)
{
 //should validate user input
 Document document = GetDocument(Request.QueryString["id"]);
 //We have the document, but can't edit it!
 if (!Locker.AcquireLock(document))
 {
  //let's add it to the user's list of docs to watch
  User.CurrentUser.AddDocumentToQueue(document.DocumentId);
  Response.Redirect("DocumentList.aspx");
 }
 //ok, we have the document and CAN edit it
 //...
}

A linha chave é o local em que o documento é adicionado à fila do usuário atual, que o adiciona à sessão. Em seguida, vamos criar um controle de usuário que irá notificá-lo quando o documento na fila ficar disponível. Esse controle pode ser colocado em qualquer página. Esse controle de usuário contém um único método AJAX, juntamente com o código necessário para registrar a classe no AJAX.

'VB.NET
Private Sub Page_Load(s As Object, e As EventArgs) 
                                   Handles MyBase.Load
 Ajax.Utility.RegisterTypeForAjax(GetType(UnlockNotifier))
End Sub

'Loops through the queued documents and checks if they're available
<Ajax.AjaxMethod()> _
Public Function GetUnlockedDocuments() As DocumentCollection
 'Get all the queued document ids belonging to the user
 Dim queuedDocument As ArrayList = User.CurrentUser.DocumentQueue
 Dim unlocked As DocumentCollection = New DocumentCollection

 For Each documentId As Integer In queuedDocumentIds
 'If the queued document is no longer locked
  If Not Locker.IsLocked(documentId) Then
   unlocked.Add(Document.GetDocumentById(documentId))
  End If
 Next

 Return unlockedDocuments
End Function

Tudo que precisamos agora é um pouco de JavaScript para fazer a solicitação e tratar da resposta. Vamos colocar as informações do documento liberado, se houver, dentro de uma tabela criada dinamicamente, com base na resposta. Para isso, começaremos com o HTML.

<div id="notifyBox" style="display:none;">
 <b>The following queued documents can now be edited</b>
 <table cellpadding="5" cellspacing="0" 
       border="0" style="border:1px solid #EEE;" 
       id="notifyTable">
  </table>
</div>

Usamos a marca DIV para ocultar tudo quando não houver documentos disponíveis (ou talvez nenhum esteja em fila para o usuário) e a marca TABLE para exibir os resultados. Vamos usar um sistema de pesquisa para determinar se existem documentos em fila disponíveis. Basicamente, isso significa que vamos chamar repetidamente o método do lado do servidor, com um atraso, e exibir os resultados. A primeira chamada ocorrerá simplesmente quando a página for carregada e as chamadas subseqüentes serão programadas para ocorrer a cada X segundos.

<script language="javascript">
window.setTimeout("PollQueue();", 2000);
//fires every 2 seconds to check if a queued document was released
//in a real system with a lot of users, 2 seconds might put too high
//a load on the server. We could even check first to see if the user
//even has anything queued, but we'll certainly need to do some
//performance testing
function PollQueue()
{
 //UnlockNotifier is the type we registered with Ajax.NET
 //GetUnlockedDocuments is a method within that type marked with
 //the AjaxMethod attribute
 UnlockNotifier.GetUnlockedDocuments(PollQueue_CallBack);
 //calls itself every 2 seconds
 window.setTimeout("PollQueue();", 2000);
}
</script>

Resta apenas tratar da resposta. Isso é semelhante ao código do exemplo anterior. Em primeiro lugar, verifique se existem erros, obtenha a resposta, faça loop nos documentos disponíveis e crie o HTML dinamicamente, neste caso incluindo linhas e colunas na tabela.

function PollQueue_CallBack(response)
{
  var notifyBox = document.getElementById("notifyBox");
  var notifyTable = document.getElementById("notifyTable");
  //if we couldn't find our table notification box
  if (notifyBox == null || notifyTable == null)
  {
    return;
  }
  //if the server-side code threw an exception
  if (response.error != null)
  { 
    notifyBox.style.display = "none"; 
    alert(response.error); //we should probably do better than this
    return;
  }  
    
  var documents = response.value;
  //if the response wasn't what we expected    
  if (documents == null || typeof(documents) != "object")
  {
    notifyBox.style.display = "none";
    return;  
  }  
  for (var i = 0; i < notifyTable.rows.length; ++i)
  {
   notifyTable.deleteRow(i);
  }
  for(var i = 0; i < documents.length; ++i)
  {    
    var row = notifyTable.insertRow(0);
    row.className = "Row" + i%2;
    var cell = row.insertCell(0);
    cell.innerHTML = documents[i].Title;
    cell = row.insertCell(1);
    var date = documents[i].Created;
    cell.innerHTML = date.getDay() + "/" + date.getMonth() 
                     + "/" + date.getYear();
    cell = row.insertCell(2);
    cell.innerHTML = "<a href='DocumentEdit.aspx?id=" 
                     + documents[i].DocumentId + "'>edit</a>";
  } 
  notifyBox.style.display = "block"; 
}

O último aprimoramento rápido que vamos examinar é como desbloquear automaticamente um documento sendo editado se o usuário fechar seu navegador, for para outro link ou clicar no botão Voltar. Normalmente, isso é conseguido conectado-se ao evento OnBeforeUnLoad ou evento OnUnload de JavaScript, abrindo um novo pop-up pequeno que faz alguma limpeza no carregamento de página e fecha a si mesmo. Embora o seu próprio uso de pop-ups possa ser legítimo, outros podem não funcionar tão bem, resultando em bloqueios de pop-up e documentos que ficam eternamente bloqueados. Para solucionar esse problema, ainda vamos usar os dois eventos de JavaScript, mas em vez de iniciar um pop-up, executaremos um método do lado do servidor por meio de AJAX. Na página usada para editar um documento, aquela que coloca um bloqueio, incluímos um pouco de JavaScript simples.

<script language="javascript">
//Makes sure the document is unlocked if the user closes the 
//browser or hits the back button
window.onbeforeunload = ReleaseLock;
function ReleaseLock() {
 Locker.ReleaseDocument(<%=DocumentID%>);
}
</script>

Aqui, DocumentId é uma variável definida e configurada em código por trás. Como alternativa, podemos armazenar DocumentId na sessão e acessá-la no ReleaseDocument do lado do servidor. ReleaseDocument basicamente remove o documento da lista de documentos bloqueados.

Exemplo 3: Pesquisa de assunto em fóruns

O último exemplo que vamos examinar será a modificação de um aplicativo existente. Ouvi essa idéia ser proposta pela primeira vez por Josh Ledgard como um recurso para os fóruns do MSDN. O objetivo é tentar auxiliar os usuários com perguntas, bem como restringir o número de postagens duplicadas. Basicamente, quando um usuário vai a um fórum fazer uma pergunta, ele insere um assunto e uma pergunta, freqüentemente sem pesquisar para saber se a pergunta já foi feita e respondida. Aqui entra o AJAX. Quando o usuário termina de inserir o assunto (e sai do campo), nós pesquisamos assimetricamente os fóruns, com base no assunto, e apresentamos discretamente os resultados ao usuário. Algumas vezes os resultados podem ser úteis, outras não.

Para conseguir isso, modificaremos o CommunityServer, o ganhador do prêmio asp.NETPRO Reader's Choice para melhor aplicativo de fóruns. Os exemplos para download não incluem o código nessa seção (ou os fóruns), mas podemos obter mais informações sobre o CommunityServer em http://communityserver.org/ e aplicar a eles os trechos de código a seguir.

Com o CommunityServer instalado e a Ajax.NET configurada (as referências e o manipulador adicionados a web.config), precisaremos fazer apenas algumas alterações para obter a funcionalidade desejada. Em primeiro lugar, vamos para o arquivo CreateEditPost.cs no projeto CommunityServerForums. Imagine isso como o code-behind da página que os usuários acessam para adicionar uma nova postagem. Vamos incluir aqui nossa função habilitada para AJAX.

//C#
[Ajax.AjaxMethod()]
public static ArrayList Search(string search)
{
 SearchQuery query = new SearchQuery();
 query.PageIndex = 0; //get the first 10 results
 query.PageSize = 10;
 query.UserID = Users.GetUser().UserID;
 query.SearchTerms = search;
 return new ForumSearch().GetSearchResults(query).Posts;
}

Podemos aproveitar a funcionalidade de pesquisa já incorporada no CommunityServer e simplesmente incluí-la na nossa função. Como sempre, o tipo precisa ser registrado na Ajax.NET. Faremos isso na função InitializeSkin do mesmo arquivo (imagine isso como Page_Load).

//C#
Ajax.Utility.RegisterTypeForAjax(typeof(CreateEditPost));

Antes de podermos pular para o JavaScript, precisamos fazer uma última alteração no lado do servidor. As classes personalizadas retornadas para a Ajax.NET, como ForumPost contida na ArrayList que estamos retornando, devem ser marcadas com o atributo Serializable. Para isso, basta entrar no arquivo Components/ForumPost.cs do projeto CommunityServerForums e adicionar o atributo.

//C#
[Serializable]
public class ForumPost : Post
{
 ...
}

No lado da apresentação, precisamos somente modificar Themes/default/Skins/View-EditCreatePost.cs no projeto CommunityServerWeb. Em primeiro lugar, vamos nos conectar ao evento onBlur da caixa de texto do assunto.

<asp:textbox onBlur="Search(this.value);" 
             id="PostSubject" runat="server" ... />

Em seguida, escreveremos o método Search de JavaScript para que chame Search do lado do servidor.

var oldValue = '';
function Search(value)
{ 
  //don't search again for something we just searched
  //would happen if the user is tabbing back and forth
  if (value != oldValue)
  {
   CreateEditPost.Search(value, Search_CallBack);
   oldValue = value;
  }
}

Por fim, resta apenas tratar da resposta. Como o exemplo anterior mostrava um modo ligeiramente mais elegante de exibir resultados em uma tabela, vamos simplesmente criar algum HTML dinâmico e introduzi-lo em um DIV fictício.

function Search_CallBack(response)
{
 //since the search functionality automatically redirects if there 
 //are no results, we can't rely on response.error

 var results = response.value;
 //if we didn't get a result
 if (results == null)
 {
  return;
 }

 //a div that we'll use to put our results
 var someDiv = document.getElementById("someDiv");  
 var html = "";
 for (var i = 0; i < results.length; ++i)
 {
  var result = results[i];
  html += "<a target=_blank href='" + result.PostID
  html += "/ShowPost.aspx'>";   
  html += result.Subject;
  html += "</a><br />"
 }
 someDiv.innerHTML = html;
}

Fazendo pequenas modificações em três arquivos (além do arquivo web.config da configuração) do aplicativo CommunityServer, conseguimos adicionar alguma funcionalidade bem elegante. Entretanto, tenha cuidado ao simplesmente adicionar funcionalidade habilitada para AJAX a um aplicativo existente. A classe ForumSearch preexistente que faz a pesquisa real pode não ter sido projetada para o tipo de uso que introduzimos. Nosso código provavelmente resultará na realização de diversas pesquisas extras e o impacto pode ser significativo.

AJAX e você

Como e onde o AJAX se encaixa nos seus aplicativos, se eles já existem ou não, é bastante individual. Embora eu tenha demonstrado como é fácil criar soluções habilitadas para AJAX com a Ajax.NET, outras considerações devem ser avaliadas. Uma preocupação séria é o impacto sobre a arquitetura geral e a capacidade de manutenção do seu aplicativo. O AJAX pode tornar ainda mais indistinta a linha entre as camadas do sistema, especialmente as camadas de apresentação, lógica de apresentação e comercial. Isso não é um problema no AJAX em si, mas no modo como você o utilizará. Contanto que esteja ciente de como é fácil causar algum sangramento entre as camadas, e que faça isso somente de modo calculado, tudo estará bem.

Um aplicativo que usa AJAX é mais difícil de manter? A resposta depende em grande parte da quantidade de JavaScript que você já está usando e da sua perícia em organizar e manter o aplicativo. Muitos desenvolvedores consideram mais difícil escrever, testar e depurar JavaScript (não devido ao próprio JavaScript, mas ao suporte de ferramenta e conhecimento de desenvolvedor). Se atualmente você estiver implementando uma lista suspensa vinculada usando JavaScript e alternar para AJAX, a manutenção do seu código provavelmente ficará mais fácil (o suporte de Ajax.NET para tipos e matrizes .NET é uma grande razão para isso). Entretanto, se escolher o caminho de postagem, provavelmente estará introduzindo uma linguagem inteiramente nova no seu aplicativo (JavaScript) e precisará lidar com a questão de ter alguns dados que não participam de ViewState (como vimos no evento de clique de botão).

Outra consideração é o impacto que AJAX terá sobre a usabilidade do seu site. Embora a qualidade fundamental de AJAX seja criar interfaces com maior capacidade de resposta, os desenvolvedores devem ter dois fatos em mente. O primeiro e mais óbvio é que AJAX depende de JavaScript. É sabido que alguns usuários desabilitam JavaScript e que alguns padrões (como o padrão do governo canadense Common Look and Feel [508 do Canadá]) requerem que os sites funcionem com ou sem JavaScript. Então, você não deve presumir que a funcionalidade de AJAX estará funcionando, e deve fazer seu aplicativo retornar ao processamento mais normal da Web caso ela não estiver disponível. Em segundo lugar, os usuários podem estranhar os aplicativos AJAX (mesmo que sejam melhores) por seu modo de utilização ser diferente daquele ao qual estão acostumados em seus aplicativos. Um exemplo disso é que uma página que executa diversas funções através de AJAX pode não se comportar como um usuário imagina que deveria com o botão Voltar, o menu Favoritos e outros recursos de navegador.

Conclusão

AJAX não é apenas uma aclamada tecnologia de vanguarda; em vez disso, é uma estrutura concreta que oferece a você soluções alternativas para os seus problemas do dia-a-dia para a criação de aplicativos da Web. A Ajax.NET torna fácil para os desenvolvedores de ASP.NET adotarem o AJAX. Os três exemplos examinados, juntamente com os projetos para download, têm como objetivo ajudá-lo a compreender como usar o AJAX e a Ajax.NET, e também proporcionam a você um espaço para experimentar algumas das suas próprias idéias. O AJAX significa mais do que criar aplicativos elegantes e sofisticados; ele pode realmente resultar em maior satisfação do cliente e vantagem competitiva. Alguns dos conceitos de alto nível sendo discutidos para o Atlas podem melhorar de maneira significativa os produtos que fornecemos. Pessoalmente, as melhores implementações de AJAX que já vi são bem leves e parecem certas. Suas próprias implementações devem ser uma experiência igualmente positiva para os seus usuários. No entanto, não se esqueça que, dependendo do problema, o AJAX não será a única solução e pode não ser a melhor. Agora, vamos provar que a comunidade ASP.NET não fica atrás de nenhuma outra e limpar a casa.

Sobre o autor

Karl Seguin usa boa parte do seu tempo nos grupos de notícias do Microsoft ASP.NET ajudando outros desenvolvedores e procurando tópicos úteis sobre os quais escrever. Quando ele não está trabalhando ou ajudando, está jogando e eliminando implacavelmente a praga Gnomish de Azeroth.

© .