CRUD em AdvPL ASP – Parte 03

Introdução

No post anterior (CRUD em AdvPL ASP – Parte 02), foi mostrado um pouco da forma como as páginas de uma aplicação WEB foram e são atualizadas e o que é o AJAX, e a primeira verão — apenas desing — da página da Agenda em HTML. Agora, vamos começar a adaptar alguns fontes da Agenda para utilizá-los no AdvPL ASP.

Separar processamento da interface

Este é um dos mandamentos do bom desenvolvimento de sistemas. O projeto do CRUD em AdvPL, com foco em uma agenda de contatos, foi escrito da forma mais simples de desenvolver um sistema: O mesmo código é responsável pela interface e pelo processamento e acesso aos dados.

Embora seja uma das formas mais simples e rápidas de desenvolver, o processamento depende das interface, e isso acaba dobrando o trabalho para você criar uma nova interface para fazer as mesmas tarefas. No exemplo do CRUD, o processo em execução é persistente, isto é, o SmartClient possui uma conexão com o Protheus Server, e nesta conexão o arquivo da agenda é mantido aberto e posicionado, e a conexão da aplicação é mantida junto com o seu contexto até o usuário finalizar a interface.

Já em uma aplicação Web, cada chamada de atualização de interface cria uma nova conexão com o servidor de aplicação, que pode ser atendida por qualquer um dos processos (também chamados de Working Threads, ou processos de trabalho), então não há persistência do contexto de execução. Existem apenas as variáveis de seção (HTTPSESSION), onde você pode armazenar informações que são exclusivas daquela seção de navegação, que como vimos nos tópicos sobre AdvPL ASP, podem ser usadas por exemplo para guardar a existência por exemplo de uma seção de login.

Logo, como o paradigma da nova interface não parte de uma conexão persistente, precisamos desamarrar o processamento da interface, e criar algum mecanismo de persistência de um mínimo de contexto, ou emular a persistência de um contexto, para que uma ação disparada pela interface saiba de onde partir para ser executada.

A separação entre processamento e interface será feita em doses homeopáticas, vamos primeiro fazer a implementação funcionar com o que temos disponível implementando alguns pontos por vez.

Iniciando o processo de consulta

Como já temos a agenda criada em AdvPL, e com uma base preenchida, vamos começar a interface WEB pela consulta dos dados da agenda. Partimos de uma tabela no banco de dados, com dois índices previamente criados. Vamos iniciar a visualização dos dados a partir do primeiro registro da tabela em ordem alfabética. Para isso, inicialmente vamos replicar (duplicar) alguns códigos na interface WEB, para realizar as operações de consulta.

Uma vez que seja mostrada ao usuário uma tela com os dados preenchidos, o processamento daquela requisição terminou. Logo, quando eu clicar por exemplo no botão “Próximo” — para mostrar o próximo registro da agenda — eu preciso pelo menos saber qual era o registro que estava sendo visualizado, para eu saber de onde partir para buscar o próximo registro.

Eu até poderia usar variáveis de seção (HTTSESSION), mas como o objetivo é minimizar o uso destas variáveis, eu posso criar dentro do meu formulário — por exemplo — um input em HTML, do tipo “hidden” — ou escondido — onde eu coloco o numero do registro atual que eu estou visualizando, no momento que eu gerei a página a ser retornada para o Browse.

Conectando com o DBAccess

Antes de mais nada, eu preciso que as minhas Working Threads tenham acesso ao Banco de Dados. Lembram-se da função U_ASPINIT(), executada para preparar cada um dos processos para AdvPL ASP? Vamos alterá-la para ela estabelecer logo de cara uma conexão com o DBAccess, que será mantida no processo e aproveitada por todas as requisições que precisarem usar o SGDB. Esta função fica no fonte ASPThreads.prw

User Function ASPInit()
Local nTopHnd

SET DATE BRITISH
SET CENTURY ON

nTopHnd := TCLInk()
If nTopHnd < 0 
  ConsoleMsg("ASPINIT - Falha de conexão "+cValToChar(nTopHnd))
  Return .F.
Endif

SET DELETED ON

ConsoleMsg("ASPINIT - Thread Advpl ASP ["+cValToChar(ThreadID())+"] Iniciada")

Return .T.

Reparem que apenas o trecho em negrito foi acrescentado na função. Agora, vamos implementar alguns controles no programa responsável pela agenda na interface AdvPL ASP — Fonte wagenda.prw

Implementando a consulta nos fontes

Dentro da User Function WAGENDA(), responsável pela interface HTML da Agenda para AdvPL ASP, vamos abrir o alias da tabela da Agenda, caso ele ainda não esteja aberto dentro do contexto do processo de trabalho (Working Thread) atual.

If Select("AGENDA") == 0 
   USE (cFile) ALIAS AGENDA SHARED NEW VIA "TOPCONN"
   DbSetIndex(cFile+'1')
   DbSetIndex(cFile+'2')
Endif
DbSelectArea("AGENDA")

Partimos de duas premissas. A primeira é que a tabela e os índices existem. E, a segunda é que, uma vez que eu deixe o alias da tabela aberto neste processo, as próximas requisições que precisarem consultar esta tabela vão encontrar ela já aberta.

As operações feitas no CRUD em AdvPL foram numeradas. Vamos seguir a mesma numeração. Como por hora estamos implementando apenas a consulta de dados, as operações são: 4 – Primeiro contato, 5 = Contato anterior, 6 = Próximo contato e 7 = Último contato. Para este controle, vamos usar um campo input de formulário escondido no HTML, chamado “OP”. Caso este campo não seja recebido na requisição, eu assumo que a agenda está sendo aberta, e que a operação default é “4” = Primeiro registro.

If empty(HTTPPOST->OP)
  // Caso nao tenha sido informada operação, a operação 
  // default é posicionar no primeiro registro da tabela
  // em ordem alfabérica
  HTTPPOST->OP := '4'
Endif

Antes do fonte chamar a montagem da tela da agenda, encapsulada no arquivo wagenda.aph — correspondendo a chamada de função H_WAGENDA() em AdvPL — vamos tratar as quatro opções possíveis.

If HTTPPOST->OP == '4' // Primeiro

  DBSetOrder(2)
  Dbgotop()
  HTTPPOST->_SHOWRECORD := .T.

ElseIf HTTPPOST->OP == '5' // Anterior

  DBSetOrder(2)
  Dbgoto( val(HTTPPOST->RECNO) )
  DbSkip(-1)
  IF bof()
    HTTPPOST->_ERRORMSG := 'Este é o primeiro contato.'
  Endif
  HTTPPOST->_SHOWRECORD := .T.

ElseIf HTTPPOST->OP == '6' // Próximo

  DbSetOrder(2) 
  Dbgoto( val(HTTPPOST->RECNO) )
  DbSkip() 
  If Eof()
    Dbgobottom()
    HTTPPOST->_ERRORMSG := 'Este é o último contato.'
  Endif
  HTTPPOST->_SHOWRECORD := .T.

ElseIf HTTPPOST->OP == '7' // Último

  DBSetOrder(2)
  Dbgobottom() 
  HTTPPOST->_SHOWRECORD := .T.

Endif

Dentro deste fonte nós criamos duas variáveis dentro do alias virtual HTTPPOST, chamadas de _SHOWRECORD e _ERRORMSG. Estas variáveis foram criadas desta fora para servirem como containers de troca de valores entre o programa AdvPL que estamos rodando agora, e a página AdvPL ASP que vamos rodar em seguida.

Cada operação já assume que o Alias da tabela de AGENDA está aberto, e realiza o posicionamento do registro correspondente. Para fazer o posicionamento no registro anterior ou no próximo do registro mostrado no Web Browse, vamos no AdvPL ASP criar um campo do tipo INPUT HIDDEN, escondido em um formulário de controle invisível, e cada requisição de POST feita para trazer um novo registro vai receber o registro que estava sendo visto no Browse naquele momento.

Implementando no AdvPL ASP

Agora que já preparamos as operações dentro do fonte AdvPL, vamos preparar o fonte AdvPL ASP para tratar a existência destes dados e as operações solicitadas. Primeiro, dentro do fonte WAGENDA.APH, na parte de Scripts no inicio da página, vamos acrescentar duas funções novas em JavaScript.

function onLoad()
{
<%If HTTPPOST->_SHOWRECORD %>
<% aStru := DbStruct() %>
<% For nI := 1 to fCount() %>
<% If aStru[ni][2] != 'M' %>
document.getElementById("I_<%=alltrim(fieldname(nI))%>").value = "<%=cValToChar(fieldget(nI))%>";
<% Endif %>
<% Next %>
<% Endif %>
<% IF !empty(HTTPPOST->_ERRORMSG) %>
window.alert("<%=HTTPPOST->_ERRORMSG%>");
<% Endif %>
};

function CallOp(nOp)
{
document.getElementById("I_OP").value = nOp;
var f = document.getElementById("F_STATUS"); 
f.action="agenda.apw"
f.submit();
}

A primeira função chama-se onLoad(), e vamos fazer o Browse chamá-la imediatamente após a carga da página, inserindo na tag de abertura do corpo da página HTML (body) a chamada desta função:

<body onload="onLoad()" style="font-family:Courier New;font-size: 12px;background-color:rgb(128,128,128);">

O corpo da função onLoad() será montado dinamicamente para cada requisição. Seu objetivo é, quando houver a visualização de um determinado registro, este script vai encontrar todos os campos INPUT da página, destinados a mostrar os valores dos campos da Agenda, e preencher estes campos com os valores lidos do Banco de Dados.

Já a função CallOp() será chamada em JavaScript pelos botões de navegação de consulta na página HTML. Os novos botões vão ficar assim:

<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(4)">Primeiro</a></td></tr>
<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(5)">Anterior</a></td></tr>
<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(6)">Próximo</a></td></tr>
<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(7)">Último</a></td></tr>

E, finalmente, antes do final do corpo do HTML (/body), vamos acrescentar um formulário de controle, para através dele fazer as requisições via POST — para não deixar rastro, cache ou histórico no Web Browse — das operações solicitadas de navegação.

<form id="F_STATUS" action="agenda.apw" method="post">
<input type="hidden" id="I_OP" name="OP" type="text" value="">
<input type="hidden" id="I_RECNO" name="RECNO" type="text" value="<%=cValToChar(recno())%>">
</form>

Neste formulário vamos criar apenas dois campos de INPUT HIDDEN, um para guardar a operação a ser solicitada, e outro para guardar o registro que está atualmente sendo visualizado. No momento que eu pedir por exemplo o próximo registro, o botão “Próximo” vai executar a função javascript CallOp(6), que vai preencher o input “I_OP” com o valor “6” e submeter novamente o formulário para o link “agenda.apw

Desta forma, o programa U_WAGENDA() vai receber a operação e o registro anteriormente posicionado, reposicionar no registro, fazer a navegação, e se por um acaso não houver registro anterior ou próximo, será mostrada na tela uma mensagem de alerta no Browse, repare no finalzinho da função onLoad().

Implementação funcionando

Com tudo atualizado e compilado, o comportamento esperado do acesso ao link http://localhost/agenda.apw deve ser:

  • Caso o usuário não esteja logado na seção atual, será exibida a tela de login
  • Após o login, deve ser exibida a tela da agenda com o primeiro contato em ordem alfabética.
  • Os quatro botões de navegação devem funcionar atualizando a página inteira e trazendo o contato correspondente.

WEB Agenda 3

Houve mais uma alteração importante no arquivo WAGENDA.APH: Todos os campos INPUT do formulário, que antes estavam com a propriedade “disabled”, passaram para “readonly”. Caso um campo não esteja habilitado, ele sequer pode receber ou mostrar um valor novo.

Conclusão

Aos poucos vamos implementando as funcionalidades da agenda original. Normalmente eu começaria pelo mais complicado — API de Cadastro — porém, como o estado default da interface é a consulta, vamos começar por ela. No próximo post, vamos fazer a consulta atualizar a foto do contato da agenda, sem precisar gravar a imagem que está no banco de dados na pasta de publicações Web.

Agradeço a todos novamente as curtidas, compartilhamentos, comentários e afins, e lhes desejo TERABYTES DE SUCESSO !!!

Referências

 

Um comentário sobre “CRUD em AdvPL ASP – Parte 03

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s