Quer saber mais?
Não deixe escapar essa oportunidade!
Faça um treinamento para Webdeveloper na Búfalo Informática

Algorítimos de lista (Master/Detail)


Você também gostará de ler os seguintes artigos
Alguns algorítimos em ASP
Treinamento de lógica de programação
Após este arquivo você gostará de estudar :

Treinamento de ASP

Uma das formas de se estudar algorítimos avançados em ASP é através da análise dos algorítimos de lista, que guardam uma considerável complexidade. Não falo apenas de uma listagem simples, mas uma lista master detail que permita eliminação e edição de seus itens.

Vamos considerar a seguinte tabela :


Estoque
=======
IdProduto
Produto
Quant
pUNIT

Vamos começar por uma listagem simples com links para uma para uma página de detalhes, veja :

<html>
<body>

<!-- utilizamos estilos apenas para enfeitar um pouco nosso exemplo -->
<style>
a {text-decoration:none}
a:hover {color:red}
</style>

<%
dim cn,rs

set cn=createobject("adodb.connection")
set rs=createobject("adodb.recordset")

cn.open "string de conexao"
rs.open "select idproduto,produto from estoque",cn

do while not rs.eof
response.write("<a href='detalhes.asp?cod=" & rs.fields("idproduto").value & "'>")
response.write(rs.fields("produto").value)
response.write("</a>")

response.write("<br>")

rs.movenext
loop
rs.close
cn.close
set rs=nothing
set cn=nothing
%>
</body>
</html>

Com esse código temos então uma listagem de produtos com links para página de detalhes. O problema é que a transmissão de dados neste exemplo é feita via método GET. O método GET é extremamente inseguro, permite que qualquer um faça uma adulteração das informações. O melhor é realizarmos uma transmissão POST. Inicialmente podemos imaginar que teríamos que criar um botão "detalhes" para cada produto, o que nos levaria a alterar o nosso design.

Mas uma alternativa para isso é criar um formulário para cada produto mas sem botão submit. Deveremos então fazer com que o link do produto dispare uma instrução javascript, causando o submit do formulário. Como cada produto terá um formulário diferente e precisaremos manipular tais formulários via código será necessário que cada formulário possua um nome diferente, mas que possamos controlar. O ideal é utilizarmos um contador para gerar os nomes dos formulários.

<html>
<body>
<!-- utilizamos    estilos apenas para enfeitar um pouco nosso exemplo -->
   <style>
   a {text-decoration:none}
   a:hover {color:red}
   </style>
<%
dim cn,rs,iCNT
set cn=createobject("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open "string    de conexao"
rs.open "select idproduto,produto from estoque",cn
iCNT=0
do while not rs.eof    %>
   <a href="javascript:form<%=icnt %>.submit()">
   <%= rs.fields("produto").value %>
   </a>
   <form id=form<%=icnt %> method=post action="detalhes.asp">
   <input type=hidden name=cod value=<%=rs.fields("idproduto").value    %>>
   </form>
   <br>
<%
   iCNT=iCNT+1
   rs.movenext
loop
rs.close
cn.close
set rs=nothing
set cn=nothing
   %>
</body>
</html>


Observe que neste código utilizamos intercalação de código ASP tanto para tornar o código mais simples de montar como de entender.

O próximo passo é montarmos a deleção de registros. Existem várias formas de implementarmos a deleção de registros, vamos analisar algumas. De inicio, uma das formas é criar um botão "Apagar" para cada produto. Isso significa criar um novo form para cada produto, só que dessa vez com o botão submit, portanto não precisaremos nos preocupar em dar diferentes nomes aos formulários.

Vamos também evitar criar novos arquivos fazendo com que o processamento da deleção de registros seja realizado pela mesma página, não outra página. Vejamos como fica o código:

<html>
<body>
<!-- utilizamos    estilos apenas para enfeitar um pouco nosso exemplo -->
   <style>
   a {text-decoration:none}
   a:hover {color:red}
   </style>
<%
dim cn,rs,iCNT
set cn=createobject("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open "string    de conexao"

   'Se o botão deletar for pressionado realiza-se a deleção    do registro
   if request.form("cmdApagar")<>"" then
         cn.execute "delete estoque where idProduto=" & request.form("cod")
   end if

   rs.open "select idproduto,produto from estoque",cn
iCNT=0 %>
<table width=100%>
   <%
do while not rs.eof    %>
   <tr><td>
   <a href="javascript:form<%=icnt %>.submit()">
   <%= rs.fields("produto").value %>
   </a>
   <form id=form<%=icnt %> method=post action="detalhes.asp">
           <input type=hidden name=cod value=<%=rs.fields("idproduto").value    %>>
   </form>
 </td><td>
   <form method=post>
          <input type=hidden name=cod value=<%=rs.fields("idproduto").value    %>>
          <input type=submit value="Apagar" name="cmdApagar">
   </form>
   </td></tr>
   <%
   iCNT=iCNT+1
   rs.movenext
loop
   %>
</table>
<%
   rs.close
   cn.close
   set rs=nothing
   set cn=nothing
   %>
</body>
</html>
Observe que criamos uma tabela para garantir um mínimo de design em nosso exemplo. Outra forma de implementarmos a deleção de registros é utilizando checkboxes para cada produto e ao final criarmos um único botão "Apagar". Aqui encontramos mais variações na forma de implementação : podemos dar um nome distinto a cada checkbox (assim como fizemos com o formulário) ou darmos o mesmo nome a todas as checkbox, de acordo com o que decidirmos isso muda a forma de implementação da deleção. Considero bem mais simples a implementação da deleção dando o mesmo nome a todas as checkbox. Além disso cada checkbox deve ter como value o código do produto a ser deletado, fazendo com que este seja transmitido para a página destino (a mesma página no caso).

Mas não podemos posicionar form dentro de form, no caso, o form dos links dentro do form de deleção, então teremos que retirar o formulário gerado para cada link e aprimorar o código javascript dos links, que precisará alterar o action do form (já que no caso dos links o action é detalhes.asp e preencher um campo hidden com o código do produto clicado, além de dar submit. Veja como fica :

<html>
<body>
<!-- utilizamos    estilos apenas para enfeitar um pouco nosso exemplo -->
   <style>
   a {text-decoration:none}
   a:hover {color:red}
   </style>
<%
dim cn,rs,iCNT
set cn=createobject("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open "string    de conexao"

   'Se o botão deletar for pressionado realiza-se a deleção    do registro
   if request.form("cmdApagar")<>"" then
        cn.execute "delete estoque where idProduto in (" & request.form("cod")    & ")"
   end if

   rs.open "select idproduto,produto from estoque",cn
iCNT=0 %>
<form method=post id=form1>
   <table width=100%>
   <%
do while not rs.eof    %>
   <tr><td>
   <a href='javascript:form1.cod.value=<%=rs.fields("idproduto").value    %>;form1.action="detalhes.asp";form1.submit()'>
   <%= rs.fields("produto").value %>
   </a>
   
   </td><td>
   <input type=checkbox name=cod value=<%=rs.fields("idproduto").value    %> >
   </td></tr>
   <%
   iCNT=iCNT+1
   rs.movenext
loop
   %>
</table>

   <input type=submit value="Apagar" name="cmdApagar">
   <input type=hidden name=cod id=cod>
</form>

   <%
   rs.close
   cn.close
   set rs=nothing
   set cn=nothing
   %>
</body>
</html>
Observe que o ICNT não foi necessário neste exemplo, mas o mantivemos, pois será útil nos seguintes.

Quando vários campos de um formulário possuem o mesmo nome, no momento de serem transmitidos o próprio browser se encarrega de juntar seus valores em um único, separando os itens com , . Poderíamos separar os valores e fazer um "delete" para cada registro, mas um truque interessante é o fato de que não precisamos : A clausula IN no SQL nos permite concatenar diretamente o valor recebido (cod) no SQL, de forma a realizar a deleção de todos os registros marcados de uma vez só.

O próximo passo é exibirmos a quantidade contida na tabela e permitirmos a alteração de seu valor. A quantidade então terá que ser exibida na forma de uma textbox. Poderíamos exibir um botão "Alterar" para cada produto ou um botão alterar único. Como já temos o form de deleção com um botão deletar único, não criaremos um novo form, aproveitaremos o formulário existente também para realizar a alteração.

Essa listagem, porém, pode conter dezenas de registros. Quando o submit ocorre todas as caixas de quantidade serão transmitidas, porém apenas as que tiverem realmente sido alteradas precisarão ser atualizadas no banco. Deveremos então identificar, entre todas as caixas, quais foram alteradas, quais não. Para fazermos isso precisaremos guardar o valor original das quantidades e processar uma comparação no ASP para identificarmos quais foram alteradas. Havendo alteração, realizamos o update.

Para analisarmos as quantidades, identificando quais foram alteradas, temos duas opções : Podemos dar um nome diferente a cada caixa de texto, utilizando um contador, que na verdade já temos, ou podemos dar o mesmo nome às caixas de texto e gerar vetores utilizando a função split, posteriormente analisando o conteúdo dos vetores.

Vamos ver cada um dos exemplos, primeiramente dando nomes diferentes para as caixas de texto. Neste exemplo, para facilitar o processamento criamos um campo contendo o total de itens existentes :


<html>
<body>
<!-- utilizamos    estilos apenas para enfeitar um pouco nosso exemplo -->
<style>
   a {text-decoration:none}
   a:hover {color:red}
</style>
<%
dim cn,rs,iCNT,iCNT2
set cn=createobject("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open "string    de conexao"

   'Se o botão alterar for pressionado realiza-se a alteração    dos registros que tiveram quantidades alteradas
   if request.form("cmdalterar")<>"" then
           'Faz um laço através de todas as caixas de quantidade
		   for iCNT2=1 to request.form("totquant")
			   'Compara a quantidade da caixa de texto com a original, para descobrir se o    usuário alterou a quantidade
			   if request.form("txtquant" & icnt2)<>request.form("orgQuant"    & iCNT2) then
				   cn.execute "update estoque set quant=" & request.form("txtquant"    & icnt2) & " where idProduto=" & request.form("codproduto"    & icnt)
			   end if
		   next
   end if
   
'Se o botão    deletar for pressionado realiza-se a deleção do registro
   if request.form("cmdApagar")<>"" then
	   cn.execute "delete estoque where idProduto in (" & request.form("cod")    & ")"
   end if

   rs.open "select idproduto,produto,quant from estoque",cn
iCNT=1 %>
<form method=post id=form1>
<table width=100%>
   <tr><td>Produto</td><td>Quant.</td><td>Deletar</td></tr>
   <%
do while not rs.eof    %>
   <tr><td>
   <a href='javascript:form1.cod.value=<%=rs.fields("idproduto").value    %>;form1.action="detalhes.asp";form1.submit()'>
   <%= rs.fields("produto").value %>
   </a>
 </td><td>
   <input type=text name=txtquant<%=iCNT %> value=<%=rs.fields("quant").value    %>>
   <input type=hidden name=orgQuant<%=iCNT %> value=<%=rs.fields("quant").value    %>>
   <input type=hidden name=codProduto<%=iCNT %> value=<%=rs.fields("idproduto").value    %> >
   </td><td>
   <input type=checkbox name=cod value=<%=rs.fields("idproduto").value    %> >
   </td></tr>
   <%
   iCNT=iCNT+1
   rs.movenext
loop
   %>

</table>

   <input type=hidden name=totQuant value=<%=iCNT -1 %>>
   <input type=submit value="Alterar" name="cmdAlterar">
   <input type=submit value="Apagar" name="cmdApagar">
   <input type=hidden name=cod id=cod>
</form>

   <%
   rs.close
   cn.close
   set rs=nothing
   set cn=nothing
   %>
</body>
</html>

Observe que inserimos também um título na tabela para melhorar levemente o design. Vejamos agora como fica sem o contador, com todas as caixas tendo o mesmo nome. Observe que não precisaremos guardar o total de itens :


<html>
<body>
<!-- utilizamos    estilos apenas para enfeitar um pouco nosso exemplo -->
<style>
   a {text-decoration:none}
   a:hover {color:red}
</style>
<%
dim cn,rs,iCNT,iCNT2,vetQuant,vetOrig,vetCod
set cn=createobject("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open "string    de conexao"

   'Se o botão alterar for pressionado realiza-se a alteração    dos registros que tiveram quantidades alteradas
   if request.form("cmdalterar")<>"" then
	   'Gera os vetores com as informações
	   vetQuant=split(request.form("txtquant"),",")
	   vetOrig=split(request.form("orgQuant"),",")
	   vetCod=split(request.form("codProduto"),",")
	 'Faz o laço    de acordo com o tamanho dos vetores. Qualquer vetor serve, já que todos    tem o mesmo tamanho
	   for iCNT2=0 to ubound(vetQuant)
		   'Compara a quantidade da caixa de texto com a original, para descobrir se o    usuário alterou a quantidade
		   if vetQuant(icnt2)<>vetOrig(icnt2) then
			   cn.execute "update estoque set quant=" & vetquant(icnt2) &    " where idProduto=" & vetCod(icnt2)
		   end if
	   next
   end if
   
'Se o botão    deletar for pressionado realiza-se a deleção do registro
   if request.form("cmdApagar")<>"" then
	   cn.execute "delete estoque where idProduto in (" & request.form("cod")    & ")"
   end if

   rs.open "select idproduto,produto,quant from estoque",cn
iCNT=1 %>
<form method=post id=form1>
<table width=100%>
   <tr><td>Produto</td><td>Quant.</td><td>Deletar</td></tr>
   <%
do while not rs.eof    %>
   <tr><td>
   <a href='javascript:form1.cod.value=<%=rs.fields("idproduto").value    %>;form1.action="detalhes.asp";form1.submit()'>
   <%= rs.fields("produto").value %>
   </a>

   </td><td>
   <input type=text name=txtquant value=<%=rs.fields("quant").value    %>>
   <input type=hidden name=orgQuant value=<%=rs.fields("quant").value    %>>
   <input type=hidden name=codProduto value=<%=rs.fields("idproduto").value    %> >
   </td><td>
   <input type=checkbox name=cod value=<%=rs.fields("idproduto").value    %> >
   </td></tr>
   <%
   iCNT=iCNT+1
   rs.movenext
loop
   %>
</table>

	<input type=submit value="Alterar" name="cmdAlterar">
   <input type=submit value="Apagar" name="cmdApagar">
   <input type=hidden name=cod id=cod>
</form>

   <%
   rs.close
   cn.close
   set rs=nothing
   set cn=nothing
   %>
</body>
</html>


Como pode observar, os algorítimos de lista nos mostram diversas formas diferentes de tratar as informações na interface Web.

 

Dennes Torres
MCSD,MCSE,MCDBA