Mostrando postagens com marcador Técnicas de desenvolvimento. Mostrar todas as postagens
Mostrando postagens com marcador Técnicas de desenvolvimento. Mostrar todas as postagens

quarta-feira, 15 de abril de 2009

Validação de CPF e CNPJ com Javascript - Delphi/Intraweb

Mais um da série ... (da séria série ...)
Ok, já fizemos vários artigos sobre como usar Javascript no Intraweb. Tenho gastado tempo registrando essa abordagem por vários motivos:

Um deles é (uma tecla que venho batendo bastante) a importância de buscar um equilíbrio, distribuído responsabilidades entre client-side e server-side (obviamente, me refiro ao contexto restrito os sistemas desenvolvidos para plataforma web). Um segundo motivo pode ser: “Como fazer isso, se eu uso Delphi/Intraweb?” Na medida em que essa é uma questão que considero possuir alguma relevância. Outro motivo, porem, trata sobre “Qual implicação, ou quais implicações, terei ao percorrer este caminho?” Tenho procurado, nos assuntos abordados, trazer alguns elementos que apontem para a responder a essa questão.
Justamente, esse último ponto, sobre as implicações, ou seja, falar sobre algumas vantagens e desvantagens, é um dos objetivos desse texto. No Intraweb existem certas particularidades ao se aplicar o Javascript, uma delas é o evento “onSubmit” do form definido numa página web. Veremos, então, sobre isso mais adiante.


Neste artigo pretendo mostrar também como podemos usar a passagem por parâmetro, de uma função Javascript, para a referência de um objeto “html” de um form no Intraweb. Seguindo a metodologia de ir do mais simples para o mais complexo, iniciaremos nossa parte prática com a criação de uma função para validação no lado cliente. Validação essa para a qual atribuiremos a responsabilidade de exemplificar esse uso de parâmetros, ao qual me referi, em Javascript, relacionado aos objetos Intraweb.
Quero usar um exemplo que seja prático, ordinário, para um cenário de sistemas de informação. Portanto, vamos criar uma função para validar CPF, uma outra para validar CNPJ e uma terceira função que vai opcionalmente validar CNPJ, CPF, ou os dois simultaneamente. Desta forma teremos uma função polivalente (a qual servirá para validar ambos os casos), cujo o processamento será realizado no lado do cliente.


Javascript para validar CNPJ, CPF no Delphi/Intraweb

Inicie um novo projeto Intraweb no Delphi, adicione no IWForm1 um IWEdit e três IWButtons. Em seguida, defina uma área de declarações de constantes e digite conforme ilustrado abaixo:


implementation

uses IWBaseForm;

{$R *.dfm}
const
AQuebraLinhaJsZN = #13;
AFncCriticaCIC =
' function CriticaCIC(ZnNumCic, ZnCnpjBool, ZnCpfBool){ ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com verifica CPF/CNPJ");' + AQuebraLinhaJsZN +
' var CicValidoZn = false; ' + AQuebraLinhaJsZN +
' ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' if(!ZnVerificaCnpj(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CNPJ Inválido"); ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else if (!ZnCnpjBool && ZnCpfBool){ ' + AQuebraLinhaJsZN +
' if (!ZnVerificaCPF(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CPF inválido"); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' ;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else { ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCnpj(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(CicValidoZn){ ' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN +
' else{ ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCPF(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(!CicValidoZn){ ' + AQuebraLinhaJsZN +
' alert("CIC Inválido") ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' var ZnMsg = ""; ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPJ ou CNPJ"; }' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CNPJ"; }' + AQuebraLinhaJsZN +
' if (!ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPF"; }' + AQuebraLinhaJsZN +
' alert("Valor Validado Ok, " +ZnMsg + ": " + ZnNumCic.value);' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN;

AFncCriticaCPF =
' function ZnVerificaCPF (ZnCpfObj){ ' + AQuebraLinhaJsZN +
' var gmSort = new Array(ZnCpfObj.value.length); ' + AQuebraLinhaJsZN +
' for (i=0; i< ZnCpfObj.value.length; i++){gmSort[i] = ZnCpfObj.value.charAt(i);} ' + AQuebraLinhaJsZN +
' gmSort.sort(); ' + AQuebraLinhaJsZN +
' if (gmSort[0] == gmSort[gmSort.length -1]){ ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com: Os números são iguais: " + ZnCpfObj.value); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' ZnAuxCal = 0; ' + AQuebraLinhaJsZN +
' for (ZnCont = 0; ZnCont < 9; ZnCont ++) ' + AQuebraLinhaJsZN +
' ZnAuxCal += parseInt(ZnCpfObj.value.charAt(ZnCont)) * (10 - ZnCont); ' + AQuebraLinhaJsZN +
' DgVZn = 11 - (ZnAuxCal % 11); ' + AQuebraLinhaJsZN +
' if (DgVZn == 10 || DgVZn == 11) ' + AQuebraLinhaJsZN +
' DgVZn = 0; ' + AQuebraLinhaJsZN +
' if (DgVZn != parseInt(ZnCpfObj.value.charAt(9))) ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' ZnAuxCal = 0; ' + AQuebraLinhaJsZN +
' for (ZnCont = 0; ZnCont < 10; ZnCont ++) ' + AQuebraLinhaJsZN +
' ZnAuxCal += parseInt(ZnCpfObj.value.charAt(ZnCont)) * (11 - ZnCont); ' + AQuebraLinhaJsZN +
' DgVZn = 11 - (ZnAuxCal % 11); ' + AQuebraLinhaJsZN +
' if (DgVZn == 10 || DgVZn == 11) ' + AQuebraLinhaJsZN +
' DgVZn = 0; ' + AQuebraLinhaJsZN +
' if (DgVZn != parseInt(ZnCpfObj.value.charAt(10))){ ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' //alert("O CPF INFORMADO É VÁLIDO."); ' + AQuebraLinhaJsZN +
' return true; ' + AQuebraLinhaJsZN +
'} ' + AQuebraLinhaJsZN;

AFncCriticaCNPJ =
' function ZnVerificaCnpj(ZNObjInput){' + AQuebraLinhaJsZN +
' var ZnDigitos, ZnDg, ZnSum, ZnCount;' + AQuebraLinhaJsZN +
' var ZnResult, ZnPos, tamanho;' + AQuebraLinhaJsZN +
' var ZnAuxCNPJ = ZNObjInput.value.replace(/\D+/g, "");' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' if (ZnAuxCNPJ.length != 14){' + AQuebraLinhaJsZN +
' ZNObjInput.focus();' + AQuebraLinhaJsZN +
' return false;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' var gmSort = new Array(ZNObjInput.value.length); ' + AQuebraLinhaJsZN +
' for (i=0; i< ZNObjInput.value.length; i++){gmSort[i] = ZNObjInput.value.charAt(i);} ' + AQuebraLinhaJsZN +
' gmSort.sort(); ' + AQuebraLinhaJsZN +
' if (gmSort[0] == gmSort[gmSort.length -1]){ ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com: Os números são iguais: " + ZNObjInput.value); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' ZnAuxTamanho = ZnAuxCNPJ.length - 2' + AQuebraLinhaJsZN +
' ZnDigitos = ZnAuxCNPJ.substring(0,ZnAuxTamanho);' + AQuebraLinhaJsZN +
' ZnDg = ZnAuxCNPJ.substring(ZnAuxTamanho);' + AQuebraLinhaJsZN +
' ZnSum = 0;' + AQuebraLinhaJsZN +
' ZnPos = ZnAuxTamanho - 7;' + AQuebraLinhaJsZN +
' for (ZnCount = ZnAuxTamanho; ZnCount >= 1; ZnCount--){' + AQuebraLinhaJsZN +
' ZnSum += ZnDigitos.charAt(ZnAuxTamanho - ZnCount) * ZnPos--;' + AQuebraLinhaJsZN +
' if (ZnPos < 2)' + AQuebraLinhaJsZN +
' ZnPos = 9;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' ZnResult = ZnSum % 11 < 2 ? 0 : 11 - ZnSum % 11;' + AQuebraLinhaJsZN +
' if (ZnResult != ZnDg.charAt(0)){' + AQuebraLinhaJsZN +
' ZNObjInput.focus();' + AQuebraLinhaJsZN +
' return false;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' ZnAuxTamanho = ZnAuxTamanho + 1;' + AQuebraLinhaJsZN +
' ZnDigitos = ZnAuxCNPJ.substring(0,ZnAuxTamanho);' + AQuebraLinhaJsZN +
' ZnSum = 0;' + AQuebraLinhaJsZN +
' ZnPos = ZnAuxTamanho - 7;' + AQuebraLinhaJsZN +
' for (ZnCount = ZnAuxTamanho; ZnCount >= 1; ZnCount--){' + AQuebraLinhaJsZN +
' ZnSum += ZnDigitos.charAt(ZnAuxTamanho - ZnCount) * ZnPos--;' + AQuebraLinhaJsZN +
' if (ZnPos < 2)' + AQuebraLinhaJsZN +
' ZnPos = 9;}' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' ZnResult = ZnSum % 11 < 2 ? 0 : 11 - ZnSum % 11;' + AQuebraLinhaJsZN +
' if (ZnResult != ZnDg.charAt(1)){' + AQuebraLinhaJsZN +
' ZNObjInput.focus();' + AQuebraLinhaJsZN +
' return false;}' + AQuebraLinhaJsZN +
' else {' + AQuebraLinhaJsZN +
' return true;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN;


Dando continuidade, para usarmos o que acabamos de definir devemos usar as propriedades específicas dos componentes Intraweb destinadas a trabalhar com Javascript. Elas existem tanto no IWForm (Formulário Delphi para web), quanto nos controles, IWControls.


Evento OnCreate do IWFrom1:



procedure TIWForm1.IWAppFormCreate(Sender: TObject);
begin
(* Atribuindo a propriedade "JavaScript" do IWForm1 as
funções que serão processadas pelo browser*)
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCPF);
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCNPJ);
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCIC);
Self.JavaScript.Add('');
// O IWButton1 vai validar as duas opções CPF e CNPJ
IWButton1.Caption := 'Valida CPF e CNPJ';
IWButton1.Width := 200;
IWButton1.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'true']));
//O IWButton2 vai validar apenas CPF
IWButton2.Caption := 'Valida CPF';
IWButton2.Width := 200;
IWButton2.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'false', 'true']));
//O IWButton3 vai validar apenas CNPJ
IWButton3.Caption := 'Valida CNPJ';
IWButton3.Width := 200;
IWButton3.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'false']));
end;


No trecho de código acima, podemos observar, comparando com os exemplos postados nos artigos anteriores, sobre Intraweb e Javascript, que o uso de parâmetros nas funções Javascript para referenciar os objetos do form (na página html), reduz sensivelmente a o número de linhas. Isso melhora a legibilidade do código, consequentimente aumenta a manutenibilidade do mesmo. Visto que, não precisamos usar o temperamental “document.all” (sensível a diversidade de navegadores). Também não precisamos mais do “document.forms[x]”, isso nos abstém da preocupação em referenciar o form específico (Lembra, no Intraweb 8, o framework pode criar vários forms na mesma página?). Obviamente, isso é possível porque a chamada a função esta sendo feita no mesmo form proprietário do objeto que está sendo passado como argumento. Em outras palavras: O objeto que está sendo passado como parâmetro na função critica CIC 2, está definido no mesmo form no qual a função esta sendo chamada.
Entretanto, infelizmente, essa abordagem pode encontrar limitações. Vejamos adiante ....

Sobre as funções de validação de CNPJ e CPF, não estou preocupado com a otimização da lógica que elas implementam. Basiei-me em alguns dos milhares de exemplos que podemos encontrar na web. Com certeza, fiz alguns ajustes. Mas nada que mereça grande atenção, embora elas funcionem corretamente, no sentido de serem eficientes em alcançarem o objetivo que se propõem. Reiterando, o propósito dessa parte do artigo é exemplificar como resolver via Javascript uma validação bastante necessária, uma situação comum, presumo, em muitos sistemas (desenvolvidos com Intraweb).


OnClick do IWButton chamar mais de uma função Javascript no evento “onSubmit” do form


Supondo que o IWButton, ou qualquer outro controle que seja, já possua uma chamada a uma função Javascript. Hipoteticamente, imagine um cenário onde o mesmo IWButton, que vai submeter o form da página web, precisa, no mesmo evento “OnClick”, validar o CIC e fazer qualquer outro tipo de validação.
Note que estou, propositalmente, construindo uma situação onde o click do botão está relacionado a ação de submeter o form da página web. Além disso, estamos trabalhando com um grau de complexidade onde você precisa (ou precisará) executar mais de um função em reposta ação do usuário clicar esse botão. O botão “Submit” (no caso, nosso IWButton1). Ora, grau de complexidade? Isso que você descreveu, Gerson, me parece tão comum! Concordo, é muito comum se você está trabalhando com outra tecnologia. No Intraweb, lamentavelmente isso não é trivial.
Então, prosseguindo, vejamos: Em primeiro lugar, vamos criar a segunda função (a qual será responsável por tornar o valor do IWEdit1 obrigatório). Ou seja, para submeter o form da página web, o usuário será obrigado a digitar algum valor no IWEdit1. Feito isso, teremos construído todos elementos necessários para a experiência que irá confirmar minha argumentação (assim espero ....). Portanto, na seção “const” (que definimos anteriormente) codificaremos, em forma de outra constante, a string com a função supracitada. Digite conforme ilustrado abaixo:




AFncValorRequerido =
' function ValorRequerido(){ ' + AQuebraLinhaJsZN +
' var ObjInput = document.getElementById("%s); ' + AQuebraLinhaJsZN +
' var AFriendlyName = "%s"; ' + AQuebraLinhaJsZN +
' if (ObjInput.value == ""){ ' + AQuebraLinhaJsZN +
' alert("É obrigatório o valor para o campo: " + AFriendlyName); ' + AQuebraLinhaJsZN +
' ObjInput.style.backgroundColor = "#FD88AA"; ' + AQuebraLinhaJsZN +
' ObjInput.focus(); ' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' else { ' + AQuebraLinhaJsZN +
' ObjInput.style.backgroundColor = "#FFFFFF"; ' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN;


Em seguida faremos uma pequena modificação na primeira função que criamos, “CriticaCIC”. No caso, ela é a segunda constante de definimos para o exemplo anterior, a “AFncCriticaCIC”. Espere, não é exatamente isso o que faremos. O que quero realmente fazer é copiar a constante “AFncCriticaCIC”, a partir disso, definir uma nova com o mesmo conteúdo. Porque, desta forma posso fazer as alterações que preciso, sem perder completamente o exemplo anterior. Logo, assim sendo, quem estiver acompanhando este artigo poderá facilmente testar e verificar a diferença entra as duas estratégias (“do grego, "Στρατηγικη"... ...no inglês, "strategy"... ...no francês, "stratégie"... ZzZzZzZzzzz...... ...Senhor leitor, o senhor está dormindo? Senhor leitor, tenha bondade..”. (Parafraseando Capitão Nascimento)

Prosseguindo (era brincadeira), retomemos: Criaremos então a nova constante a partir da cópia de “AFncCriticaCIC”:


AFncCriticaCIC2 =
' function CriticaCIC2(ZnCnpjBool, ZnCpfBool){ ' + AQuebraLinhaJsZN +
' var ZnNumCic = document.getElementById("%s); ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com verifica CPF/CNPJ");' + AQuebraLinhaJsZN +
' var CicValidoZn = false; ' + AQuebraLinhaJsZN +
' ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' if(!ZnVerificaCnpj(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CNPJ Inválido"); ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else if (!ZnCnpjBool && ZnCpfBool){ ' + AQuebraLinhaJsZN +
' if (!ZnVerificaCPF(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CPF inválido"); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' ;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else { ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCnpj(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(CicValidoZn){ ' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN +
' else{ ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCPF(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(!CicValidoZn){ ' + AQuebraLinhaJsZN +
' alert("CIC Inválido") ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' var ZnMsg = ""; ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPJ ou CNPJ"; }' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CNPJ"; }' + AQuebraLinhaJsZN +
' if (!ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPF"; }' + AQuebraLinhaJsZN +
' alert("Valor Validado Ok, " +ZnMsg + ": " + ZnNumCic.value);' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN;

Essa nova versão da crítica de “CIC” possui um parâmetro a menos. Eu retirei o argumento referente ao objeto do form (input que recebe o valor digitado do suposto CIC). Já que decidimos não perder o que foi feito no exemplo anterior, vamos adicionar mais um IWButton. O IWButton4, servirá, nessa segunda parte do artigo, para chamar a nova critica CIC (o conteúdo da AFncCriticaCIC2). Feito isso, vamos acrescentar mais umas linhas de código no evento “OnCreate” do IWForm1.



(* segunda parte do artigo:
OnClick do IWButton chamar mais de uma função Javascript *)
IWEdit1.FriendlyName := 'CIC: "CNPJ" ou "CPF"';
Self.JavaScript.Add('//**************************************');
Self.JavaScript.Add('//segunda parte do artigo:' + #13 +
'// OnClick do IWButton chamar mais de uma função Javascript');
Self.JavaScript.Add(Format(AFncCriticaCIC2, [UpperCase(IWEdit1.Name)]));
Self.JavaScript.Add('');

Self.JavaScript.Add('');
Self.JavaScript.Add(Format(AFncValorRequerido, [UpperCase(IWEdit1.Name),
IWEdit1.FriendlyName]));
Self.JavaScript.Add('//*******www.estacaozn.blogspot.com**********');

IWButton4.Caption := 'Valor Obrigatório & Valida CIC';
IWButton4.Width := 360;
IWButton4.ScriptEvents.Add('onClick').EventCode.Add(
'ValorRequerido() && CriticaCIC(true, true)');


No Intraweb, chamar, efetivamente, no evento “onClick”, mais de um função pode ser feito declarando o nome das funções, juntamente com o operador lógico adequado, ao caso específico, na propriedade “ScriptEvent” do componente escolhido (No nosso caso o escolhido foi o TIWButton). Provavelmente, essa não deve ser a única forma de se fazer isso, essa é apenas uma (suponho ;-)). Especificamente a que eu encontrei no momento para alcançar meu objetivo.

Cabe registrar que, como alternativa, para propriedade “ExtraTagParams” existe um restrição. Só funciona se a ação não envolver o submit do form. Como posso ter certeza do acabei de afirmar?
Para exemplificar, respondendo a pergunta anterior, vamos submeter o form. Contudo, para isso, precisamos pelo menos processar algo no servidor. Com essa finalidade, codificaremos qualquer coisa dummy no evento OnClick do componente Delphi/Intraweb TIWButton4. Adicione um novo componente, um IWLabel, em seguida, acompanhe trecho de código abaixo:


procedure TIWForm1.IWButton4Click(Sender: TObject);
begin
IWLabel1.Font.Size := 16;
IWLabel1.Caption := Format('Valor digitado válido: %s', [IWEdit1.Text]); ;
end;


Veja, como o Intraweb monta a função que submete o form:


var IWLABEL1IWCL = null;
var IWBUTTON1IWCL = null;
var IWEDIT1IWCL = null;
var IWBUTTON2IWCL = null;
var IWBUTTON3IWCL = null;
var IWBUTTON4IWCL = null;
function IWBUTTON4_onclick(event) {
return ValorRequerido() && CriticaCIC2(true, true)
}

function IWBUTTON4_onclick0(event) {
return SubmitClickConfirm('IWBUTTON4','', true, '');
}

Ok, finalizamos! Podemos testar, F9 .. e manda ver ...
Prova dos Nove

Considero que terminamos o assunto aqui. Se por acaso você inda quer conferir o que eu afirmei acima prossiga:
Agora vamos testar se realmente, de fato, quando esta situação acontece, não é possível ter sucesso usando a propriedade “ExtraTagParams”.
Adicionaremos mais um IWButton, no meu exemplo esse será o IWButton5. Retornaremos ao evento “OnCreate” do IWForm1 (Unit1) e codificaremos a chamada ao Javascript de crítica de valor requerido e validação de CIC, na propriedade ExtraTagParams do último IWButton adicionado. Veja abaixo como ficou:



procedure TIWForm1.IWAppFormCreate(Sender: TObject);
begin
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCPF);
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCNPJ);
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCIC);
Self.JavaScript.Add('');
// O IWButton1 vai validar as duas opções CPF e CNPJ
IWButton1.Caption := 'Valida CPF e CNPJ';
IWButton1.Width := 200;
IWButton1.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'true']));
//O IWButton2 vai validar apenas CPF
IWButton2.Caption := 'Valida CPF';
IWButton2.Width := 200;
IWButton2.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'false', 'true']));
//O IWButton3 vai validar apenas CNPJ
IWButton3.Caption := 'Valida CNPJ';
IWButton3.Width := 200;
IWButton3.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'false']));

(* segunda parte do artigo:
OnClick do IWButton chamar mais de uma função Javascript *)
IWEdit1.FriendlyName := 'CIC: CNPJ ou CPF';
Self.JavaScript.Add('//**************************************');
Self.JavaScript.Add('//segunda parte do artigo:' + #13 +
'// OnClick do IWButton chamar mais de uma função Javascript');
Self.JavaScript.Add(Format(AFncCriticaCIC2, [UpperCase(IWEdit1.Name)]));
Self.JavaScript.Add('');

Self.JavaScript.Add('');
Self.JavaScript.Add(Format(AFncValorRequerido, [UpperCase(IWEdit1.Name),
IWEdit1.FriendlyName]));
Self.JavaScript.Add('//*******www.estacaozn.blogspot.com**********');

IWButton4.Caption := 'Valor Obrigatório & Valida CIC';
IWButton4.Width := 360;
IWButton4.ScriptEvents.Add('onClick').EventCode.Add(
'return ValorRequerido() && CriticaCIC2(true, true)');

(* testando chamar duas funções Javascript na propriedade ExtraTagParams *)

IWButton5.ExtraTagParams.Add(
Format('onClick=return ValorRequerido() && CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'true']));
end;


O trecho que acabamos de digitar inicia com o comentário da linha 48. Note que fizemos a chamada semelhante ao IWButton4. Da forma como está agora, funciona perfeitamente. Mesmo que eu faça a chamada, no IWButton5, igualzinho a do IWButton4, funciona.


IWButton5.ExtraTagParams.Add(
Format('onClick=return ValorRequerido() && CriticaCIC2(true, true)',
[UpperCase(IWEdit1.Name)]));


Todavia, se eu envolver o IWButton5 no submit do form teremos fracasso. Babau! A vaca vai pro brejo.
Para conferir basta codificar qualquer coisa no evento “OnClick” do IWButton5. Isso irá forçar um submit para que esse código seja processado no servidor. Vou colocar uma dummy aqui ... veja:


procedure TIWForm1.IWButton5Click(Sender: TObject);
begin
IWLabel1.Caption := IWEdit1.Text;
WebApplication.ShowMessage('Se você esta codificando esse exemplo corretamente, '+
'essa mensagem nunca não vai ser exibida. Jamais será! Garanto (heheh ..).');
end;


O Intraweb tenta ter controle total de alguns eventos do objeto form da página html. Isso acontece de forma mais rigorosa no evento “onLoad”. Eu até entendo a razão deles fazerem isso, mas não a considero justificável, e justamente por isso discordo desse tipo de controle. Acho isso extremamente prejudicial ao desenvolvimento de sites, sistemas, serviços, web com o Intraweb. Ao mesmo tempo isso demonstra de forma muito clara o quanto esse framework é frágil e imaturo.

Abaixo, o código integral da Unit1, seguido do código da página html gerado pelo Intraweb:


unit Unit1;

interface

uses
Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, IWCompEdit,
Controls, IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl, IWControl,
IWCompButton, IWCompLabel;

type
TIWForm1 = class(TIWAppForm)
IWButton1: TIWButton;
IWEdit1: TIWEdit;
IWButton2: TIWButton;
IWButton3: TIWButton;
IWButton4: TIWButton;
IWLabel1: TIWLabel;
IWButton5: TIWButton;
procedure IWButton5Click(Sender: TObject);
procedure IWButton4Click(Sender: TObject);
procedure IWAppFormCreate(Sender: TObject);
public
end;

implementation


{$R *.dfm}
const
AQuebraLinhaJsZN = #13;
AFncCriticaCIC =
' function CriticaCIC(ZnNumCic, ZnCnpjBool, ZnCpfBool){ ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com verifica CPF/CNPJ");' + AQuebraLinhaJsZN +
' var CicValidoZn = false; ' + AQuebraLinhaJsZN +
' ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' if(!ZnVerificaCnpj(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CNPJ Inválido"); ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else if (!ZnCnpjBool && ZnCpfBool){ ' + AQuebraLinhaJsZN +
' if (!ZnVerificaCPF(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CPF inválido"); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' ;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else { ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCnpj(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(CicValidoZn){ ' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN +
' else{ ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCPF(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(!CicValidoZn){ ' + AQuebraLinhaJsZN +
' alert("CIC Inválido") ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' var ZnMsg = ""; ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPJ ou CNPJ"; }' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CNPJ"; }' + AQuebraLinhaJsZN +
' if (!ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPF"; }' + AQuebraLinhaJsZN +
' alert("Valor Validado Ok, " +ZnMsg + ": " + ZnNumCic.value);' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN;

AFncCriticaCPF =
' function ZnVerificaCPF (ZnCpfObj){ ' + AQuebraLinhaJsZN +
' var gmSort = new Array(ZnCpfObj.value.length); ' + AQuebraLinhaJsZN +
' for (i=0; i< ZnCpfObj.value.length; i++){gmSort[i] = ZnCpfObj.value.charAt(i);} ' + AQuebraLinhaJsZN +
' gmSort.sort(); ' + AQuebraLinhaJsZN +
' if (gmSort[0] == gmSort[gmSort.length -1]){ ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com: Os números são iguais: " + ZnCpfObj.value); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' ZnAuxCal = 0; ' + AQuebraLinhaJsZN +
' for (ZnCont = 0; ZnCont < 9; ZnCont ++) ' + AQuebraLinhaJsZN +
' ZnAuxCal += parseInt(ZnCpfObj.value.charAt(ZnCont)) * (10 - ZnCont); ' + AQuebraLinhaJsZN +
' DgVZn = 11 - (ZnAuxCal % 11); ' + AQuebraLinhaJsZN +
' if (DgVZn == 10 || DgVZn == 11) ' + AQuebraLinhaJsZN +
' DgVZn = 0; ' + AQuebraLinhaJsZN +
' if (DgVZn != parseInt(ZnCpfObj.value.charAt(9))) ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' ZnAuxCal = 0; ' + AQuebraLinhaJsZN +
' for (ZnCont = 0; ZnCont < 10; ZnCont ++) ' + AQuebraLinhaJsZN +
' ZnAuxCal += parseInt(ZnCpfObj.value.charAt(ZnCont)) * (11 - ZnCont); ' + AQuebraLinhaJsZN +
' DgVZn = 11 - (ZnAuxCal % 11); ' + AQuebraLinhaJsZN +
' if (DgVZn == 10 || DgVZn == 11) ' + AQuebraLinhaJsZN +
' DgVZn = 0; ' + AQuebraLinhaJsZN +
' if (DgVZn != parseInt(ZnCpfObj.value.charAt(10))){ ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' //alert("O CPF INFORMADO É VÁLIDO."); ' + AQuebraLinhaJsZN +
' return true; ' + AQuebraLinhaJsZN +
'} ' + AQuebraLinhaJsZN;

AFncCriticaCNPJ =
' function ZnVerificaCnpj(ZNObjInput){' + AQuebraLinhaJsZN +
' var ZnDigitos, ZnDg, ZnSum, ZnCount;' + AQuebraLinhaJsZN +
' var ZnResult, ZnPos, tamanho;' + AQuebraLinhaJsZN +
' var ZnAuxCNPJ = ZNObjInput.value.replace(/\D+/g, "");' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' if (ZnAuxCNPJ.length != 14){' + AQuebraLinhaJsZN +
' ZNObjInput.focus();' + AQuebraLinhaJsZN +
' return false;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' var gmSort = new Array(ZNObjInput.value.length); ' + AQuebraLinhaJsZN +
' for (i=0; i< ZNObjInput.value.length; i++){gmSort[i] = ZNObjInput.value.charAt(i);} ' + AQuebraLinhaJsZN +
' gmSort.sort(); ' + AQuebraLinhaJsZN +
' if (gmSort[0] == gmSort[gmSort.length -1]){ ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com: Os números são iguais: " + ZNObjInput.value); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' ZnAuxTamanho = ZnAuxCNPJ.length - 2' + AQuebraLinhaJsZN +
' ZnDigitos = ZnAuxCNPJ.substring(0,ZnAuxTamanho);' + AQuebraLinhaJsZN +
' ZnDg = ZnAuxCNPJ.substring(ZnAuxTamanho);' + AQuebraLinhaJsZN +
' ZnSum = 0;' + AQuebraLinhaJsZN +
' ZnPos = ZnAuxTamanho - 7;' + AQuebraLinhaJsZN +
' for (ZnCount = ZnAuxTamanho; ZnCount >= 1; ZnCount--){' + AQuebraLinhaJsZN +
' ZnSum += ZnDigitos.charAt(ZnAuxTamanho - ZnCount) * ZnPos--;' + AQuebraLinhaJsZN +
' if (ZnPos < 2)' + AQuebraLinhaJsZN +
' ZnPos = 9;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' ZnResult = ZnSum % 11 < 2 ? 0 : 11 - ZnSum % 11;' + AQuebraLinhaJsZN +
' if (ZnResult != ZnDg.charAt(0)){' + AQuebraLinhaJsZN +
' ZNObjInput.focus();' + AQuebraLinhaJsZN +
' return false;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' ZnAuxTamanho = ZnAuxTamanho + 1;' + AQuebraLinhaJsZN +
' ZnDigitos = ZnAuxCNPJ.substring(0,ZnAuxTamanho);' + AQuebraLinhaJsZN +
' ZnSum = 0;' + AQuebraLinhaJsZN +
' ZnPos = ZnAuxTamanho - 7;' + AQuebraLinhaJsZN +
' for (ZnCount = ZnAuxTamanho; ZnCount >= 1; ZnCount--){' + AQuebraLinhaJsZN +
' ZnSum += ZnDigitos.charAt(ZnAuxTamanho - ZnCount) * ZnPos--;' + AQuebraLinhaJsZN +
' if (ZnPos < 2)' + AQuebraLinhaJsZN +
' ZnPos = 9;}' + AQuebraLinhaJsZN +
'' + AQuebraLinhaJsZN +
' ZnResult = ZnSum % 11 < 2 ? 0 : 11 - ZnSum % 11;' + AQuebraLinhaJsZN +
' if (ZnResult != ZnDg.charAt(1)){' + AQuebraLinhaJsZN +
' ZNObjInput.focus();' + AQuebraLinhaJsZN +
' return false;}' + AQuebraLinhaJsZN +
' else {' + AQuebraLinhaJsZN +
' return true;' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN;

AFncValorRequerido =
' function ValorRequerido(){ ' + AQuebraLinhaJsZN +
' var ObjInput = document.getElementById("%s"); ' + AQuebraLinhaJsZN +
' var AFriendlyName = "%s"; ' + AQuebraLinhaJsZN +
' if (ObjInput.value == ""){ ' + AQuebraLinhaJsZN +
' alert("É obrigatório o valor para o campo: " + AFriendlyName); ' + AQuebraLinhaJsZN +
' ObjInput.style.backgroundColor = "#FD88AA"; ' + AQuebraLinhaJsZN +
' ObjInput.focus(); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' else { ' + AQuebraLinhaJsZN +
' ObjInput.style.backgroundColor = "#FFFFFF"; ' + AQuebraLinhaJsZN +
' }' + AQuebraLinhaJsZN +
' return true;}' + AQuebraLinhaJsZN;

AFncCriticaCIC2 =
' function CriticaCIC2(ZnCnpjBool, ZnCpfBool){ ' + AQuebraLinhaJsZN +
' var ZnNumCic = document.getElementById("%s"); ' + AQuebraLinhaJsZN +
' alert("www.estacaozn.blogspot.com verifica CPF/CNPJ");' + AQuebraLinhaJsZN +
' var CicValidoZn = false; ' + AQuebraLinhaJsZN +
' ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' if(!ZnVerificaCnpj(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CNPJ Inválido"); ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else if (!ZnCnpjBool && ZnCpfBool){ ' + AQuebraLinhaJsZN +
' if (!ZnVerificaCPF(ZnNumCic)){ ' + AQuebraLinhaJsZN +
' alert("CPF inválido"); ' + AQuebraLinhaJsZN +
' return false; ' + AQuebraLinhaJsZN +
' ;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' else { ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCnpj(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(CicValidoZn){ ' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN +
' else{ ' + AQuebraLinhaJsZN +
' CicValidoZn = ZnVerificaCPF(ZnNumCic); ' + AQuebraLinhaJsZN +
' if(!CicValidoZn){ ' + AQuebraLinhaJsZN +
' alert("CIC Inválido") ' + AQuebraLinhaJsZN +
' return false;} ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' } ' + AQuebraLinhaJsZN +
' var ZnMsg = ""; ' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPJ ou CNPJ"; }' + AQuebraLinhaJsZN +
' if (ZnCnpjBool && !ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CNPJ"; }' + AQuebraLinhaJsZN +
' if (!ZnCnpjBool && ZnCpfBool) { ' + AQuebraLinhaJsZN +
' ZnMsg = "CPF"; }' + AQuebraLinhaJsZN +
' alert("Valor Validado Ok, " +ZnMsg + ": " + ZnNumCic.value);' + AQuebraLinhaJsZN +
' return true;} ' + AQuebraLinhaJsZN;

procedure TIWForm1.IWAppFormCreate(Sender: TObject);
begin
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCPF);
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCNPJ);
Self.JavaScript.Add('');
Self.JavaScript.Add(AFncCriticaCIC);
Self.JavaScript.Add('');
// O IWButton1 vai validar as duas opções CPF e CNPJ
IWButton1.Caption := 'Valida CPF e CNPJ';
IWButton1.Width := 200;
IWButton1.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'true']));
//O IWButton2 vai validar apenas CPF
IWButton2.Caption := 'Valida CPF';
IWButton2.Width := 200;
IWButton2.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'false', 'true']));
//O IWButton3 vai validar apenas CNPJ
IWButton3.Caption := 'Valida CNPJ';
IWButton3.Width := 200;
IWButton3.ExtraTagParams.Add(
Format('onClick=CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'false']));

(* segunda parte do artigo:
OnClick do IWButton chamar mais de uma função Javascript *)
IWEdit1.FriendlyName := 'CIC: CNPJ ou CPF';
Self.JavaScript.Add('//**************************************');
Self.JavaScript.Add('//segunda parte do artigo:' + #13 +
'// OnClick do IWButton chamar mais de uma função Javascript');
Self.JavaScript.Add(Format(AFncCriticaCIC2, [UpperCase(IWEdit1.Name)]));
Self.JavaScript.Add('');

Self.JavaScript.Add('');
Self.JavaScript.Add(Format(AFncValorRequerido, [UpperCase(IWEdit1.Name),
IWEdit1.FriendlyName]));
Self.JavaScript.Add('//*******www.estacaozn.blogspot.com**********');

IWButton4.Caption := 'Valor Obrigatório & Valida CIC';
IWButton4.Width := 360;
IWButton4.ScriptEvents.Add('onClick').EventCode.Add(
'return ValorRequerido() && CriticaCIC2(true, true)');

(* testando chamar duas funções Javascript na propriedade ExtraTagParams *)

(* IWButton5.ExtraTagParams.Add(
Format('onClick=return ValorRequerido() && CriticaCIC(%s, %s, %s)', [UpperCase(IWEdit1.Name),
'true', 'true'])); *)

IWButton5.ExtraTagParams.Add(
Format('onClick=return ValorRequerido() && CriticaCIC2(true, true)',
[UpperCase(IWEdit1.Name)]));
end;

procedure TIWForm1.IWButton4Click(Sender: TObject);
begin
IWLabel1.Font.Size := 16;
IWLabel1.Caption := Format('Valor digitado válido: %s', [IWEdit1.Text]); ;
end;

procedure TIWForm1.IWButton5Click(Sender: TObject);
begin
IWLabel1.Caption := IWEdit1.Text;
WebApplication.ShowMessage('Se você esta codificando esse exemplo corretamente, '+
'essa mensagem nunca não vai ser exibida. Jamais será! Garanto (heheh ..).');
end;
(* esse trecho para Intrweb 8*)
initialization
TIWForm1.SetAsMainForm;


Abaixo, trecho código Javascript gerado pelo Intraweb como resultado do processamento do que implementamos em Delphi.



function ZnVerificaCPF (ZnCpfObj){
var gmSort = new Array(ZnCpfObj.value.length);
for (i=0; i< ZnCpfObj.value.length; i++){gmSort[i] = ZnCpfObj.value.charAt(i);}
gmSort.sort();
if (gmSort[0] == gmSort[gmSort.length -1]){
alert("www.estacaozn.blogspot.com: Os números são iguais: " + ZnCpfObj.value);
return false;
}
ZnAuxCal = 0;
for (ZnCont = 0; ZnCont < 9; ZnCont ++)
ZnAuxCal += parseInt(ZnCpfObj.value.charAt(ZnCont)) * (10 - ZnCont);
DgVZn = 11 - (ZnAuxCal % 11);
if (DgVZn == 10 || DgVZn == 11)
DgVZn = 0;
if (DgVZn != parseInt(ZnCpfObj.value.charAt(9)))
return false;
ZnAuxCal = 0;
for (ZnCont = 0; ZnCont < 10; ZnCont ++)
ZnAuxCal += parseInt(ZnCpfObj.value.charAt(ZnCont)) * (11 - ZnCont);
DgVZn = 11 - (ZnAuxCal % 11);
if (DgVZn == 10 || DgVZn == 11)
DgVZn = 0;
if (DgVZn != parseInt(ZnCpfObj.value.charAt(10))){
return false;}
//alert("O CPF INFORMADO É VÁLIDO.");
return true;
}


function ZnVerificaCnpj(ZNObjInput){
var ZnDigitos, ZnDg, ZnSum, ZnCount;
var ZnResult, ZnPos, tamanho;
var ZnAuxCNPJ = ZNObjInput.value.replace(/\D+/g, "");

if (ZnAuxCNPJ.length != 14){
ZNObjInput.focus();
return false;
}
var gmSort = new Array(ZNObjInput.value.length);
for (i=0; i< ZNObjInput.value.length; i++){gmSort[i] = ZNObjInput.value.charAt(i);}
gmSort.sort();
if (gmSort[0] == gmSort[gmSort.length -1]){
alert("www.estacaozn.blogspot.com: Os números são iguais: " + ZNObjInput.value);
return false;
}

ZnAuxTamanho = ZnAuxCNPJ.length - 2
ZnDigitos = ZnAuxCNPJ.substring(0,ZnAuxTamanho);
ZnDg = ZnAuxCNPJ.substring(ZnAuxTamanho);
ZnSum = 0;
ZnPos = ZnAuxTamanho - 7;
for (ZnCount = ZnAuxTamanho; ZnCount >= 1; ZnCount--){
ZnSum += ZnDigitos.charAt(ZnAuxTamanho - ZnCount) * ZnPos--;
if (ZnPos < 2)
ZnPos = 9;
}
ZnResult = ZnSum % 11 < 2 ? 0 : 11 - ZnSum % 11;
if (ZnResult != ZnDg.charAt(0)){
ZNObjInput.focus();
return false;
}

ZnAuxTamanho = ZnAuxTamanho + 1;
ZnDigitos = ZnAuxCNPJ.substring(0,ZnAuxTamanho);
ZnSum = 0;
ZnPos = ZnAuxTamanho - 7;
for (ZnCount = ZnAuxTamanho; ZnCount >= 1; ZnCount--){
ZnSum += ZnDigitos.charAt(ZnAuxTamanho - ZnCount) * ZnPos--;
if (ZnPos < 2)
ZnPos = 9;}

ZnResult = ZnSum % 11 < 2 ? 0 : 11 - ZnSum % 11;
if (ZnResult != ZnDg.charAt(1)){
ZNObjInput.focus();
return false;}
else {
return true;
}
}


function CriticaCIC(ZnNumCic, ZnCnpjBool, ZnCpfBool){
alert("www.estacaozn.blogspot.com verifica CPF/CNPJ");
var CicValidoZn = false;

if (ZnCnpjBool && !ZnCpfBool) {
if(!ZnVerificaCnpj(ZnNumCic)){
alert("CNPJ Inválido");
return false;}
}
else if (!ZnCnpjBool && ZnCpfBool){
if (!ZnVerificaCPF(ZnNumCic)){
alert("CPF inválido");
return false;
;}
}
else {
CicValidoZn = ZnVerificaCnpj(ZnNumCic);
if(CicValidoZn){
return true;}
else{
CicValidoZn = ZnVerificaCPF(ZnNumCic);
if(!CicValidoZn){
alert("CIC Inválido")
return false;}
}
}
var ZnMsg = "";
if (ZnCnpjBool && ZnCpfBool) {
ZnMsg = "CPJ ou CNPJ"; }
if (ZnCnpjBool && !ZnCpfBool) {
ZnMsg = "CNPJ"; }
if (!ZnCnpjBool && ZnCpfBool) {
ZnMsg = "CPF"; }
alert("Valor Validado Ok, " +ZnMsg + ": " + ZnNumCic.value);
return true;}


//**************************************
//segunda parte do artigo:
// OnClick do IWButton chamar mais de uma função Javascript
function CriticaCIC2(ZnCnpjBool, ZnCpfBool){
var ZnNumCic = document.getElementById("IWEDIT1");
alert("www.estacaozn.blogspot.com verifica CPF/CNPJ");
var CicValidoZn = false;

if (ZnCnpjBool && !ZnCpfBool) {
if(!ZnVerificaCnpj(ZnNumCic)){
alert("CNPJ Inválido");
return false;}
}
else if (!ZnCnpjBool && ZnCpfBool){
if (!ZnVerificaCPF(ZnNumCic)){
alert("CPF inválido");
return false;
;}
}
else {
CicValidoZn = ZnVerificaCnpj(ZnNumCic);
if(CicValidoZn){
return true;}
else{
CicValidoZn = ZnVerificaCPF(ZnNumCic);
if(!CicValidoZn){
alert("CIC Inválido")
return false;}
}
}
var ZnMsg = "";
if (ZnCnpjBool && ZnCpfBool) {
ZnMsg = "CPJ ou CNPJ"; }
if (ZnCnpjBool && !ZnCpfBool) {
ZnMsg = "CNPJ"; }
if (!ZnCnpjBool && ZnCpfBool) {
ZnMsg = "CPF"; }
alert("Valor Validado Ok, " +ZnMsg + ": " + ZnNumCic.value);
return true;}



function ValorRequerido(){
var ObjInput = document.getElementById("IWEDIT1");
var AFriendlyName = "CIC: CNPJ ou CPF";
if (ObjInput.value == ""){
alert("É obrigatório o valor para o campo: " + AFriendlyName);
ObjInput.style.backgroundColor = "#FD88AA";
ObjInput.focus();
return false;
}
else {
ObjInput.style.backgroundColor = "#FFFFFF";
}
return true;}

//*******www.estacaozn.blogspot.com**********

function Validate() {
return true;
}



Artigo completo (View Full Post)

segunda-feira, 15 de dezembro de 2008

Trabalhando com Controles Lookup

Trabalhando com Controles Lookup

No Delphi chamamos de “Lookup” o mecanismo que representa o relacionamento entre duas tabelas dinamicamente e de forma automatizada para a manipulação de uma delas. Isso consiste em duas operações:
A primeira é de exibir a descrição de um dado a qual não existe no registro da tabela que esta sendo manipulada.
A segunda é, ao mesmo tempo que lista a descrição, sincronizadamente, atribui o valor do campo chave, de relacionamento entra as tabelas envolvidas no lookup, para a tabela que esta sendo manipulada.



Por exemplo, vejamos o relacionamento entre duas tabelas: “Produtos” e “Categoria de Produtos” (as quais usamos no artigo anterior). Primeiro a tabela Producs, abaixo:




Temos o ID da Categoria, mas não temos a descrição dela nesta tabela. A descrição da Categoria encontra-se na tabela Categories.



Existe um relacionamento entre elas ... (Mas isso não é obrigatório para se fazer o Lookup). Essa relação entre os campos pode ser feita pela aplicação, basta que exista um relacionamento lógico entre as colunas das tabelas que serão relacionadas.



O que essa estrutura me força previamente entender? _ Devo entender que para atualizar a tabela de “Produtos” dependerei sempre dos valores existentes na tabela “Categorias”. Isso porque, os valores possíveis para a categoria de produtos está armazenado na tabela Categoria, e a constraint que define a integridade referencial entre as duas tabelas jamais permitirá que seja inserido um valor para categoryID na tabela “Products” que não exista em “Categories”. Isso é muito bom que seja dessa forma.

O Lookup permite reproduzirmos essa relação, respeitando a referencia entre as tabelas de forma automática, suprimindo totalmente a necessidade da codificação de sequer uma linha de código para isso.


Os Controles de Lookup mais comuns são:

DBLookupComboBox: TDBLookupComboBox;
DBLookupListBox: TDBLookupListBox;

Intraweb Framework
IWDBLookupComboBox: TIWDBLookupComboBox;
IWDBLookupListBox: TIWDBLookupListBox;

Dev Express
cxDBLookupComboBox: TcxDBLookupComboBox;
cxDBCheckComboBox: TcxDBCheckComboBox;

Jedi
JvDBLookupComboEdit: TJvDBLookupComboEdit;

Campo Virtual LookUp

Tfield fkLookup: A mesma lógica descrita acima é aplicada também aos Datasets, especificamente quando, através deles trabalhamos com o que a literatura chama de campos persistentes. Trata-se de objetos da classe Tfield. Quando manipulamos dados usando Datasets (TDataset) podemos criar objetos que representam a referência linha/coluna de uma tabela. Esses objetos chamamos de campos de um Dataset, eles nos permitem acessar um determinado dado de um registro. Os Tfields podem ser de vários tipos. Cada tipo determina um grupo de operações que poderá realizar o Tfield. A Propriedade FieldKind determina o tipo do Tfield.
fkData, fkCalculated, fkLookup, fkInternalCalc, fkAggregate são os valores possiveis para a propriedade FieldKind. A Classe TFieldKind, definida na unit DB, define esses valores:



type TFieldKind = (fkData, fkCalculated, fkLookup, fkInternalCalc, fkAggregate);


O Tfield usado para fazermos Lookup é do tipo “fkLookup”. Isso quer dizer que a propriedade FieldKind é “fkLookup”.

Usamos a técnica de Lookups com os Fields (TFild) de um Dataset (Um TclientDataSet por exemplo) quando existe a necessidade de acessarmos (disponibilizarmos, visualizarmos) dados provenientes de tabelas diferentes num único resultset. Como por exemplo, ao apresentar uma listagem de “Pedidos” precisamos explicitamente exibir na tela: O nome do cliente que efetuou a compra, a data do pedido e a descrição dos produtos vendidos. Obviamente, a primeira coisa que vem a mente é o recurso de utilizarmos um join em SQL.


SELECT
C.NomeCliente,
P.DataPedido,
I.Quantidade,
I.PrecoVenda,
(I.Quantidade * I.PrecoVenda) Total,
P.DescricaoProduto
FROM
Clientes C
inner join Pedidos P on
C.ClienteID = P.ClikenteID
inner join Itens I on
I.PedidoID = P.PedidoID
inner join Produtos Pr on
Pr.ProdutoID = I.ProdutosID


Contudo, num Dataset, isto impossibilitaria a utilização do mesmo para qualquer atualização nestes dados (Obviamente, isso é verdadeiro se não consideramos a técnica de gerenciarmos explicitamente, via código, o evento “BeforeUpdateRecord” do DataSetProvider – Falaremos sobre está técnica mais adiante).
No Delphi, o campo (TField) “virtual” lookUp funciona, no que tange a visualização de dados, de forma semelhante ao join. Ou seja, podemos configurar um determinado Dataset trazendo dados através de um comando SQL genérico (Select * from ) de uma única tabela, como o exemplo anterior, “Products”, e adicionarmos a ele um campo virtual que irá conter informações de um outro Dataset. A exemplo da relação entre Products e Categories, demonstrado acima. A grande vantagem desta técnica é porque mesmo disponibilizando dados de tabelas diferentes e ainda assim podermos atualizar os dados usando os comandos convencionais do próprio Dataset. Para fazermos uso do campo LookUp basta que os Datasets possuam um campo de relacionamento.

Vejamos agora, a utilização do campo LookUp:

Suponha que neste momento queremos criar um cadastro (CRUD) de produtos. Mas temos um problema, o banco apresenta um normalização para a tabela produtos de maneira que o dado categoria do produto esta em outra tabela. A tabela Categorias (no caso Categories) é uma tabela de referência. Ela não é transacional e possui um número relativamente pequeno de registros.
Lembre-se que, na hora de incluirmos novos Produtos precisamos digitar, na interface, os dados necessários da tabela de produtos, sendo que para os campos “CategoryID” e “SupplierID” presimos apresentar as descrições de cada um dos campos. Entretanto, o dado, descrição da categoria, por exemplo, encontra-se na tabela de Categories. Ou seja, temos que ler da tabela de Categories: a descrição (Categories.Description), e o valor do respectivo vampo de relacionamento (Categories.CategoryID). Para, então, atribuir a Produtos (Products.CategoryID).

Para isso usaremos um Dataset, o CdsProdutos, criaremos nele um campo lookUp que apontará para o campo Description de Categories no CdsCategorias.
Existe o campo de relacionamento entre essas duas tabelas, esses dois campos estão presentes tanto no CdsProdutos (Products.CategoryID), quanto no CdsCategorias (Categories.CategoryID) possibilitando desta forma um “join virtual”, entre CdsProdutos e CdsCategorias. Consequentimente, podemos ainda assim a atualizar o recordset do CdsProdutos. Outro bonus fundamental dessa técnica é, o que que propositalmente guardei para relembrar no final, de que com o campo LookUp a atribuição de valor ao campo “Products.CategoryID” e feita automaticamente sem a necessidade de código para isso, proveniente do link feito com Categories.CategoryID. Isso ocorre imediatamente a ação do usuário selecionar a descrição da categoria no controle lookup disponibilizado na interface.




As linhas vermelhas indicam as propriedades envolvidas na configuração de um de um controle DBLookup (DBLookupComboBox). AS Linhas verdes indicam os valores, e suas origens, que devem ser associadas as respectivas propriedades.

Criando o Campo Virtual LookUp

Com o botão direito do mouse sobre o CdsProdutos, selecione “Fields Editor ...”



Para criar um novo Field, botão direito no “Fields Editor” e selecione “New Field” (Ou digite “Ctrl + N”).




No Diálogo de definição do novo Field configure as propriedades conforme ilustrado na figura abaixo.




Na propriedade “Name” será definido o nome da coluna/campo virtual. Automaticamente, enquanto você digitar o Delphi gera o nome do objeto Tfield em “Component”. A propriedade “Type” define o tipo de dado que o Tfield irá trabalhar, internamente isso irá definir a propriedade DataType cujo o tipo é “TfieldType”, por sua vez isso refletirá na definição de uma classe que especializa o Tfield a qual o Field criado instanciará.
Dependendo do tipo escolhido em “Type” será imprescindível definir o tamanho do Field. Isso quer dizer, por exemplo, se o campo for do tipo String, o limite máximo quanto ao número de caracteres que o Field poderá armazenar. Logo, na propriedade “Size” digite um valor adequado para o tamaho, limite de caracteres, no campo “CategoriaVirt”. É mais que aconselhável você conferir no banco qual o tamanho da coluna “Categories.CategoryName”, visto que será os valores armazenados por ela listados por esse novo campo.




Em Field Type marque Lookup, em resposta a essa ação os campos de “Lookup definition” serão habilitados, em “Key Fields” e “Lookup Keys” selecionamos as colunas de relacionamento entre os Datasets envolvidos no Lookup. Todavia, para habilitar a propriedade Dataset (onde devemos definir o Dataset que disponibilizara os dados para Lookup, no nosso caso CdsCategorias), temos que selecionar primeiro a coluna do CdsProdutos que faz o relacionamento com Categorias (CategoryID).

Na propriedade DataSet, repetindo, é onde definimos o Dataset de Lookup. Selecione CdsCategorias.
Na propriedade “Lookup Keys”, selecionamos a coluna, do Dataset o qual escolhemos na propriedade “Dataset”, que se relaciona com o CdsProdutos (CategoryID).
Por fim, na propriedade “Result Field” selecionamos a coluna do CdsCategorias que servirá para listar os dados da descrição da categoria. Ou melhor, a coluna para a qual nosso campo virtual “CategoriaVirt” apontará. Selecione “CategoryName”.




Portanto, olhando a tamanho da coluna em “Disign Table” no Enterprise Manager, podemos definir o valor 15 para a propriedade “Size”.


Para finalizar, após definir todas as propriedades, click em “OK”

Se você fizer um Drag and Drop do novo Field virtual Lookup para o Form veja que será criado um controle DbLookupCombobox associado ao CdsProdutos pelo DataSource “DsProdutos”.






Para testar, adicione um DbGrid ao Form1, associe ele ao DataSource DsProdutos (através da propriedade “DataSource” do DbGrid, no Object Inspector). No Evento OnCreate Do Form1 abra os dois Cds (CdsProdutos e CdsCategorias).



procedure TForm1.FormCreate(Sender: TObject);
begin
CdsCategotias.Open;
CdsProdutos.Open;
end;




Observações:

Embora apresente um ganho extraordinário para o desenvolvimento no front-end ,em situações onde trabalhamos com CRUD que envolve tabelas normalizadas, a técnica de lookup nem sempre será adequada. Observamos diversas situações em que o emprego do Lookup trará grande inconveniência, tanto no para usabilidade quanto para outros pontos importantes como: Performance e manutenibilidade. Um caso onde o Lookup é desaconselhado é quando a tabela de lookup possui muitos registros.

Em substituição a técnica de Lookup devemos usar, em conjunto, mais de uma técnica. Um exemplo comum é o join no próprio SQL do Dataset que manipula os dados, para tratar o pacote transacional posteiormente no evento BeforeUpdateRecord do DatasetProvider. Mas isso exige conhecimento intermediário em DataSnap. Em Breve abordaremos esse tema!

Artigo completo (View Full Post)

segunda-feira, 3 de novembro de 2008

Banco de Dados ou Bando de Dados?

O fato de se gastar na ordem dos milhares por uma tecnologia de banco de dados relacional, tipo Oracle, MS SQL Server, IBM DB2, etc, não garante por si só que você terá informações consistentes.
Se você deseja algum dia extrair informações que agreguem valor ao seu negócio, trate de garantir que o que entra no seu banco de dados não seja lixo.
Supondo que, no que tange a modelagem de dados seu banco seja impecável. Suponhamos ainda, que você comprou o que existe de mais poderoso em termos de hardware, ainda assim existe a possibilidade desses dois fatores serem totalmente irrelevantes caso não exista mecanismos de checagem, neste suposto data base, a fim de garantir que o dado que é “inputado” não possui qualidade.
Para isso existem as constraints, checks e domains. Através deles programamos regras no banco de dados que trabalharão para que tudo que for inserido nele seja consistente, compreensível, relevante (Leia mais sobre ... ).

Veja um exemplo desse tipo de controle:

-- Criando a tabela de Tipo de Contato
CREATE TABLE ZnTipo_Contato(
TIPO_ContatoID NUMBER (8,0) PRIMARY KEY,
DESCRICAO VARCHAR2(20) NOT NULL,
DT_CADASTRO TIMESTAMP NOT NULL,
UpdateDate TIMESTAMP NOT NULL,
USERUPDATE NUMBER (8,0) NOT NULL,
CONSTRAINT chk_DESCRICAO CHECK (LENGTH(DESCRICAO) > 4)
);


No trecho de código acima criei uma tabela que possui nela duas constraints, ou seja, duas checagens de controle. Uma que não permitirá valores repetidos (Primary Key) na coluna “Tipo_ContatoID” (linha 3). A outra, não vai permitir valores com menos de 4 caracteres na coluna “DESCRICAO” (linha 8).

Comentários nos Objetos de Banco de Dados

Outro ponto importante que vai ser de extrema relevância para que seu banco de dados passe longe de ser o “bando de dados” mais high tech do mundo é o cuidado em comentar cada objeto construído no banco. Isso deve ser considerado uma cultura a ser desenvolvida, tanto pela equipe, num âmbito gerencial, quanto individualmente (Ou seja, aonde quer que você vá leve esse bom hábito).

Pense, com que cara você vai ficar quando daqui a três meses alguém lhe perguntar qual o significado da coluna “ValPerJurosPgto” e você não lembrar? Todavia, para a mesma situação, se houver um comentário na coluna sua orelha vai ser eternamente grata por continuar cinco palmos menor (hehehhe), além disso seu bolso irá lhe agradecer o salário continuar entrando.

COMMENT ON TABLE ZnTipo_Contato IS 'Tabela de referência. Armazena os possíveis Tipos de Contato. Exemplo: Email, Telefone, Celular';
COMMENT ON COLUMN ZnTipo_Contato.TIPO_ContatoID IS 'Primary key da tabela ZnTipo_Contato.';
COMMENT ON COLUMN ZnTipo_Contato.DESCRICAO IS 'Descrição do Tipo de Contato.';
COMMENT ON COLUMN ZnTipo_Contato.DT_CADASTRO IS 'Data de Cadastro do registro na tabela ZnTipo_Contato.';
COMMENT ON COLUMN ZnTipo_Contato.UpdateDate IS 'Data em que o usuário do sistema que atualizou um registro de Tipo de Contato.';
COMMENT ON COLUMN ZnTipo_Contato.USERUPDATE IS 'Usuário do sistema que atualizou um registro de Tipo de Contato.';


Agora vamos fazer alguns testes ...

-- Criando a Sequence para geração de Uk para a tabela ZnTipo_Contato
CREATE SEQUENCE SQ_ZnTipo_Contato
MINVALUE 1
MAXVALUE 999999999999999999999999999
START WITH 1
NOCYCLE
NOORDER
CACHE 20;



A seguir, vou inserir alguns registros na nova tabela. Pretento, em dado momento testar a constraint "chk_DESCRICAO".



-- Inserindo registros de Tipo de Contato em ZnTipo_Contato

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Email'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Telefone Residencial'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Telefone Comercial'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Telefone Celuar'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Gtalk'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Skype'
, SYSDATE
, SYSDATE
, 1
);

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Orkut Profile'
, SYSDATE
, SYSDATE
, 1
);

COMMIT;


Ok, agora vamos provocar a validação. Vou inserir um tipo de contato cujo tamanho será menor que 4 caracteres.

---- Testanto a constraint, inserindo um contato cujo o tamanho é menor que 4

INSERT INTO ZnTipo_Contato(
TIPO_CONTATOID
, DESCRICAO
, DT_CADASTRO
, UPDATEDATE
, USERUPDATE
) VALUES
(
SQ_ZnTipo_Contato.NEXTVAL
, 'Msn'
, SYSDATE
, SYSDATE
, 1
);




Verificando os registros inseridos


SELECT * FROM ZnTipo_Contato;



Como posso ver os comentários que colocamos nas colunas da tabela?

SELECT * FROM ALL_COL_COMMENTS WHERE table_name = 'ZNTIPO_CONTATO'



Artigo completo (View Full Post)

quarta-feira, 22 de outubro de 2008

Front End x Back End – UDFS no Servidor SGBD


UDF: “User Defined Function”, nos SGDBs, são funções criadas pelo desenvolvedor. Os principais fornecedores de tecnologia de banco de dados disponibilizam essa feature nos seus produtos.

O modelo Cliente/Servidor caracteriza-se por centralizar a programação referente a regra de negócio no servidor SGBD. Neste artigo, veremos como executar uma função no banco Oracle (PL/SQL) a partir da aplicação em Delphi.

No Back-And

Primeiro vamos criar a função no Oracle, a qual posteriormente evocaremos de uma interface (um aplicativo, front-end), um programa desenvolvido em Delphi.


create or replace function DeterminaMaior(ZNValor1 in NUMBER, ZNValor2 in NUMBER, ZNValor3 in NUMBER) return number is
Result number;
begin
IF (ZNValor1 > ZNValor2) AND (ZNValor1 > ZNValor3) THEN
RESULT := ZNValor1;
ELSE
IF (ZNValor2 > ZNValor1) AND (ZNValor2 > ZNValor3) THEN
RESULT := ZNValor2;
ELSE
RESULT := ZNValor3;
END IF;
END IF;
return(Result);
end DeterminaMaior;



Compile a função “DeterminaMaior” e pronto! Finalizamos aqui a parte de programação no servidor.

No Front-End

Inicie uma aplicação (no menu - File, New, Application), adicione no form1 os Seguintes componentes:

1- TADOConnection (Palheta ADO): configure a conexão com o banco na propriedade “ConnectionString”. Em seguida, Altere a propriedade “LoginPrompt” para “False”.

2- TADOStoredProc(Palheta ADO): Associe ele ao ADOConnection através da propriedade “Connection”. Em seguida, na propriedade “ProcedureName” digite o nome da função que acabamos de criar. Exemplo: “DeterminaMaior”. Pode ser conveniente digitar o nome do schema ponto o nome da função, tipo: “[eschemaName]. DeterminaMaior”.

Feto isso, click na propriedade parameters e veja alista re parâmetros que ele recupera automaticamente. Veja a ilustração abaixo:




Adicione também um TBitBtn (Palheta Additional). Na propriedade “Caption” defina: “Determinar Maior”.

Preciso adicionar três edits (TEdit – Palheta Standard) e um Memo (TMemo – Palheta Standard).




Codificando o evento OnClick do BitBtn1: Digite conforme exemplificado abaixo:


procedure TForm1.BitBtn1Click(Sender: TObject);
const
ZnConstMsg = 'www.estacaozn.blogspot.com - O Valor maior é %s';
Var
ZnMsg: String;
begin

with ADOStoredProc1 do
begin
Parameters[0].Value := 0;
Parameters[1].Value := GetValorEdit(Edit1);
Parameters[2].Value := GetValorEdit(Edit2);
Parameters[3].Value := GetValorEdit(Edit3);
ExecProc;
end;
ZnMsg := Format(ZnConstMsg,
[VarTOStr(ADOStoredProc1.Parameters[0].Value)]);
Memo1.Lines.Add(ZnMsg);
MessageDlg(ZnMsg, mtInformation, [mbOK], 0);
end;

Nas linhas 2 e 3, defini como constante o valor da mensagem a ser exibida como resultado do processamento da UDF que criamos.

Nas linhas 4 e 5, declarei uma variável para auxiliar no out put do processamento que estamos realizando. Quando recuperar o valor retornado pela UDF vamos usá-la para compor a mensagem para o usuário.

Na liha 6, a palavra reservada “begin” inidca o início do bloco de comando executados pelo procedimento “BitBtn1Click”.

Na linha 7, espaço em branco. Devemos deixar espaços em branco em alguns casos onde desejamos que, na leitura do nosso código, o entendimento fique mais fácil.

Da linha 9 a 12, passamos os valores para os parâmetros do “ADOStoredProc1”. Note que criei uma função “GetValorEdit” para poder recuperar o valor dos edits de forma segura. veremos o código dela mais adiante.

Na linha 13, chamamos o método ExecProc, de TADOStoredProc, o qual executa a UDF, “DeterminaMaior”, no banco.

Na linha 15, formatamos o valor da mensagem que deverá ser exibida composta do valor retornado do processamento no banco.

Na linha 17, adiciono ao Memo1 a mensagem.

Na linha 18, chamamos a função “MessageDlg”, API do Windows, para exibir o resultado do processamento para o usuário.


Codificando o Método “GetValorEdit”:

Na seção “private” vamos codificar a declaração método “GetValorEdit”. Segue trecho de código ilustrando o corpo do método:



function TForm1.GetValorEdit(Edt: TEdit): Double;
var
AuxNum: Double;
begin
try
AuxNum := StrToFloat(Edt.Text);
except
on EConvertError do
AuxNum := 0;
end;

Result := AuxNum;
end;


Agora execute a aplicação (pressione F9) e teste ...



A seguir, código completo da unit ...
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, DB, ADODB;

type
TForm1 = class(TForm)
BitBtn1: TBitBtn;
ADOStoredProc1: TADOStoredProc;
ADOConnection1: TADOConnection;
Memo1: TMemo;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
procedure BitBtn1Click(Sender: TObject);
private
function GetValorEdit(Edt: TEdit): Double;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.BitBtn1Click(Sender: TObject);
const
ZnConstMsg = 'www.estacaozn.blogspot.com - O Valor maior é %s';
Var
ZnMsg: String;
begin

with ADOStoredProc1 do
begin
Parameters[0].Value := 0;
Parameters[1].Value := GetValorEdit(Edit1);
Parameters[2].Value := GetValorEdit(Edit2);
Parameters[3].Value := GetValorEdit(Edit3);
ExecProc;
end;
ZnMsg := Format(ZnConstMsg,
[VarTOStr(ADOStoredProc1.Parameters[0].Value)]);
Memo1.Lines.Add(ZnMsg);
MessageDlg(ZnMsg, mtInformation, [mbOK], 0);
end;

function TForm1.GetValorEdit(Edt: TEdit): Double;
var
AuxNum: Double;
begin
try
AuxNum := StrToFloat(Edt.Text);
except
on EConvertError do
AuxNum := 0;
end;

Result := AuxNum;
end;

end.


Artigo completo (View Full Post)

segunda-feira, 8 de setembro de 2008

Webservice no BDS2006

Observamos que o assunto "WebService em Delphi" tem tido muita procura aqui no blog. Por isso, pretendo produzir mais material sobre este tema. Contudo, o assunto é abrange um escopo bastante vasto, principalmente no que tange as questões práticas do desenvolvimento de serviços baseados em XML/SOAP. Podemos citar para exemplificar alguns temas sobre “WebService e Delphi”: Testar as versões mais recentes (inclusive o Tiburon – Delphi 2009), verificar incompatibilidades (se existirem), ou diferenças. Uma coisa interessante seria a integração com outras tecnologias (Java, C# e PHP, por exemplo). Outro ponto importante: Acesso e disponibilização de dados (um CRUD, talvez, usando como camada intermediária um aplicação servidora WS). Em fim, há bastante terreno para ser trabalhado.
Neste sentido, podemos eventualmente atender alguma questão pontual demandada pelos leitores. Coloque em forma de comentários sua demanda que na medida do possível teremos prazer em atendê-la. Algumas pessoas já solicitam nosso suporte, informalmente, via e-mail. Porém, seria melhor que além do e-mail o comentário sobre a demanda fosse registrado. Pois, dessa forma aproveitamos a resolução do seu problema para conteúdo do blog. Assim, outros que por ventura vierem até nós com problemas semelhantes possam compartilhar da solução sem gerar retrabalho para nós. Conseqüentemente, imagino que nosso conteúdo será mais aderente. Isso é o que de fato nos importa.

Iniciando um novo projeto

No BDS2006 no menu File ►New ► Other ► Delphi Projects ►WebServices, selecione “SOAP Server Application”. Em seguida uma série de configurações prévias serão feitas mediante alguns diálogos.



No diálogo seguinte selecione ISAPI/NSAPI Dynamic Link Library, e click em “OK”.



Click “Yes” para criar a interface do serviço (WSDL). Veja imagem abaixo:



No próximo diálogo definiremos o nome do serviço que estamos criando: “ZnShowMeTheMoney”, conforme ilustrado abaixo.



Pronto, temos um novo projeto! Estamos desenvolvendo uma aplicação servidora. Um WebService, veja as units que foram cridas na inicialização do nosso projeto:



A lógica sobre declarações nas units, num projeto WebService: Existe uma unit para declarações de interface e uma unit para as implementarmos o que declararemos na unit interface (neste caso temos: “ZnShowMeTheMoneyIntf”, interface. “ZnShowMeTheMoneyImpl”, implementação).

Na interface declararemos uma função. Digite conforme ilustrado abaixo:


(* Declarando uma funcionalidade no serviço que estamos criando *)
function ZnShowMeTheMoney: WideString; stdcall;

A seguir, veja como ficou a unit “ZnShowMeTheMoneyIntf”.

{ Invokable interface IZnShowMeTheMoney }

unit ZnShowMeTheMoneyIntf;

interface

uses InvokeRegistry, Types, XSBuiltIns;

type

{ Invokable interfaces must derive from IInvokable }
IZnShowMeTheMoney = interface(IInvokable)
['{F002F904-9FD7-401D-80E2-037DFFAA4590}']

{ Methods of Invokable interface must not use the default }
{ calling convention; stdcall is recommended }
(* Declarando uma funcionalidade no serviço que estamos criando *)
function ZnShowMeTheMoney: WideString; stdcall;
end;

implementation

initialization
{ Invokable interfaces must be registered }
InvRegistry.RegisterInterface(TypeInfo(IZnShowMeTheMoney));

end.


Agora para implementarmos iremos fazê-lo na unit “ZnShowMeTheMoneyImpl”. Digite conforme ilustrado no trecho de código asseguir:

type

{ TZnShowMeTheMoney }
TZnShowMeTheMoney = class(TInvokableClass, IZnShowMeTheMoney)
public
(* Na unit de implementação precisamos digitar novamente a assinatura do
método que declaramos na interface "ZnShowMeTheMoneyIntf" *)
function ZnShowMeTheMoney: WideString; stdcall;
end;

implementation


Declare uma seção “uses” após a palavra reservada “implementation” e declare as bibliotecas: Math e SysUtils.

OBS: No BDS temos code insight para declaração de bibliotecas!!!



Vamos implementar o método declarado na seção public de “TZnShowMeTheMoney”.

function TZnShowMeTheMoney.ZnShowMeTheMoney: WideString;
const
ZnMsgConst = 'Here is your Money, U$ %f! Do you can see him? look at the message ... ';
var
RandomValue: Double;
RandomDiv: Integer;
begin
RandomValue := RandomRange(1, 10000);
RandomDiv := RandomRange(1, 7);
RandomValue := (RandomValue / RandomDiv);

Result := Format(ZnMsgConst, [RandomValue]);
end


A função que acabamos de codificar, “ZnShowMeTheMoney”, é simplesmente um processamento dummy que retorna aleatoriamente dois valores, os quais são usados como operandos numa divisão cujo o resultado é formatado numa string e retornado na função.
Agora vamos salvar a aplicação, veja ilustração abaixo:




Para o nome do projeto vamos usar “PrjZNShowMoney.bdsproj”. A imagem seguinte mostra, o meu “Project Manager”, para que você possa compara a estrutura do seu projeto com o andamento do nosso exemplo.



Agora vamos publicar nosso serviço: Compile o projeto, em seguida faça o deploy da DLL, “PrjZNShowMoney.dll”, no IIS.

Criei um diretório virtual com alias de “WsShowMeTheMoneyZn”. Para testar, no browser digite: http://localhost/wsshowmethemoneyzn/

Click no link WSDL e você acessará a interface do serviço “IZnShowMeTheMoneyservice”, isso que você esta vendo é mais que uma página web comum.




Neste primeiro exemplo podemos concluir que o desenvolvimento em WS no BDS2006 é semelhante ao do Delphi 7.
É Muito rápido, intuitivo e amigável desenvolver serviços RPC baseados em SOAP/XML no DBS2006, desde que você conheça bem três conceitos:

1 - A estrutura de declarações no Delphi.

2 - Calling Conventions: Convenções de chamadas (traduziremos assim)- Quando declaramos procedimentos ou funções podemos definir, neste momento, a convenção de chamada através das diretivas: register, pascal, cdecl, stdcall, and safecall.
Por exemplo:

function ZNFuncao(const Xzn, Yzn: Double): Double; cdecl;

function ZNProcedimento(const Xzn, Yzn: Double; var AoutZn: Double); stdcall;

3- Sistemas distribuídos: Também chamado na literatura de objetos distribuídos.

Outros artigos sobre Webservice e Delphi

Artigo completo (View Full Post)

 
BlogBlogs.Com.Br