Clipper On Line • Ver Tópico - Mini tutorial mod_harbour

Mini tutorial mod_harbour

Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.

Moderador: Moderadores

 

Mini tutorial mod_harbour

Mensagempor Vlademiro » 22 Ago 2020 11:55

Alteração de dados

O objetivo é alterar um dado através do formulário no banco.

Caminho feliz :) \o/

(1) Usuário altera os dados e clica no botão OK (no form)
(2) Sistema envia a variável "___pk" com o valor da chave primária + os campos preenchidos para o form.prg.
(3) save.prg recebe a variável "___pk", recebe os valores dos campos e realiza o UPDATE.
(4) navegador recebe uma mensagem de resposta, se for erro, exibe para o usuário.

Comentários :

******************************
Na parte cliente, resolvi deixar o código que trata do envio de dados do formulário em index.html

Chamei o botão do formulário de btnFormSubmit

   <button id="btnFormSubmit" type="button" class="btn btn-primary">Ok</button>


Ao clicar em btnFormSubmit

     $("#btnFormSubmit").click( function(){
          $.ajax({
           type: 'POST', <--- Agora é POST por causa dos vários campos. Pode ter upload de arquivos tb.
           url: "save.prg",
           data: $("#formData").serialize(), <--- Uma facilidade. Aqui eu pego automaticamente todos os campos do formulário.
           async: false,       
           dataType: 'json',
           success: function( data ){
            switch ( data.Result ){
             case 'OK':
               $('#formModal').modal('toggle'); <----- toogle é tipo liga/desliga. Se a janela tiver aberta, fecha. Se tiver fechada, abre.
               brw.refreshGrid();
               brw.buttonUpdate(); <--- Poderia colocar esse método dentro de refreshGrid(), acho que vou fazer isso no futuro
               break;
             case 'ERRO':  <--- Caso ocorra um erro na inserção       
               alert( data.Message ); <--- Exibe a mensagem de erro que veio do banco.
               break;

            }   
           } 
     }); // ajax
     });


*****************************
No servidor eu recebo os dados (eles são enviados pelo método POST, por isso AP_PostPairs e não AP_GetPairs)
LOCAL hPost:= AP_PostPairs()


Uma regra que eu usava desde o Clipper: eu vou querer aproveitar esse mesmo arquivo (save.prg) para tratar a inclusão também, futuramente.
Qual critério devo adotar para diferenciar uma alteração e não uma inclusão ?

Eu adotei a seguinte regra : se a chave primária "vier" então é alteração.
 IF hb_HHasKey( hPost , "___pk" )


Alguém faria diferente ?

****************************
Note que eu tenho 3 arquivos PRGs agora.

grid.prg --> retorna o JSON com o grid (paginação, etc)
form.prg --> preenche o formulário
save.prg --> salva os dados

Você pode querer fazer diferente, basta criar um padrão.

Para uma tabela de produtos, por exemplo:

produtos_grid.prg
produtos_form.prg
produtos_save.prg

Só uma ideia. Você pode querer unificar esses 3 em apenas 1, ou criar uma classe genérica, etc.

Acho que é só. O resto está no anexo.
ex16 - update.zip
(5.77 KiB) Baixado 97 vezes


Retirei do anexo o banco de dados MDB e as LIBS JQuery/Bootstrap para o arquivo não ficar muito grande. Tava com 700Kb, agora está bem menor. Para não ocupar espaço desnecessário na hospedagem. O banco de dados e as LIBS estão nos anexos anteriores.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 23 Ago 2020 16:46

Formulário de inserção

O objetivo é incluir um dado através do formulário no banco.

***********
Comentários do lado cliente

Primeiro tenho um novo botão. O botão de inclusão que coloquei no canto superior.

 <div class="row">
      <button id="btnFormInsert" class="btn btn-primary" data-toggle="modal" data-target="#formModal">Inserir</button>
   </div>


Note que ele foi vinculado ao form modal através de : data-target="#formModal"

Pronto, só isso bastaria, mas eu tenho um pequeno problema. Vou descrever abaixo :

(1) O usuário clica para ALTERAR um registro
(2) Usuário altera e clica em salvar
(3) Os dados são salvos com sucesso e o formulário é fechado.
(4) Agora o usuário clica para INCLUIR
(5) O formulário abre com os dados do último preenchimento. Esse é o erro.

Preciso "limpar" o formulário antes de abrir.

Isso pode ser feito criando um evento para quando o usuário clicar no botão de inserir.
O código abaixo percorre os ítens do formulário (each) e faz a limpeza do conteúdo de cada controle.
   $("#btnFormInsert").click( function(){  <------ Quando o usuário clicar em inserir 
        $('#formData').find(':input').each(function() { <---- Percorra os controles
            switch (this.type) { <---- Para cada tipo de controle
                case 'hidden':
               case 'password':
               case 'select-multiple':
               case 'select-one':
               case 'text':
               case 'textarea':
                  $(this).val(''); <------ Limpe o conteudo
                  break;
               case 'checkbox':
               case 'radio':
                  this.checked = false; <----- Desmarque
            }
         });
   });


Só um comentário: #formModal é a janela modal, #formData é o formulário dentro da janela modal. A limpeza é em #formData.
Poderia colocar essa função logo após a rotina de alteração, assim não precisava criar esse evento acima. Realmente me esqueci.

Também cometi outro erro que poderia ser evitado. Eu me esqueci de criar no banco de dados uma chave primária autonumerada.
Não tenho MSAccess e a ferramenta que tenho aqui é bem genérica e não tem essa opção para que eu altere o campo direto no banco.
Então, no meu script (save.prg), fiz apenas uma pequena modificação porque eu me esqueci de criar uma chave autonumerada.

cSQL := StrTran( cSQL , ":empno" , hb_ntos(hb_RandomInt(1000,9999999)) ) 


Se quiser simular um erro comente a linha acima e tente incluir um registro.

2020-08-20_122001.png


Quanto as mensagens de erro que vem do banco, você pode personalizar no banco ou na sua aplicação.
Em projetos reais você pode criar triggers no seu banco para mandar uma mensagem mais amigável para o usuário.
No PostgreSQL, por exemplo, existem comandos que param a rotina pela metade, desfazem o que foi feito e geram uma mensagem de erro.
Uma das formas seria, por exemplo :
 RAISE EXCEPTION ''ID inexistente --> %'', id_usuario;


Procure na documentação do seu banco caso queira colocar as mensagens de erro nele. Se sua aplicação for suportar vários bancos, talvez
seja melhor traduzir as mensagens no Harbour mesmo, e assim evitar a criação de várias mensagens dentro dos triggers de cada banco.

*********
É só.
A medida que vamos avançando algumas etapas ficam bem fáceis.

Essa etapa da inclusão, por exemplo, nem precisava de código, bastava colocar o botão.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Itamar M. Lins Jr. » 23 Ago 2020 17:07

Ola!
Como fica a tela de login ?
Eu tenho feito, algumas coisas. Mas a parte de proteção, acesso como fica ?
Detalhe que uso LINUX e DBF.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 23 Ago 2020 18:56

Ainda não cheguei lá.

Mas com certeza é com Cookies.
SetCookie( cName, cValue, [nSecs], [cPath], [cDomain], [lHttps], [lOnlyHttp] )   <---- creates a cookie   
GetCookies()   <---  retrieves all cookies as a hash


O navegador só reconhece esse método, que eu saiba.
Existem as sessões, mas são implementações baseadas em cookies.

Na pasta samples\sessions do modHarbour tem exemplos de login com sessões.

Basicamente é :

1.) Usuário faz login ---> sistema gera um cookie com o nome do usuário, por exemplo: SetCookie( "USER_MEUSISTEMA" , "Joao" )
2.) Usuário faz logout ---> Apaga o cookie. Não vi lá como faz para apagar.
3.) Cada página a ser protegida tem que botar uma checagem no início dela. Tipo: "USER_MEUSISTEMA" está definido ? Se estiver segue. Pode exibir o nome do usuário na tela, para saber quem está logado, etc.

Você já está bem adiantado.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 24 Ago 2020 05:25

Hoje vou fazer um ajuste na classe que gera o grid (brw.js)

Vou explicar a mudança a seguir :

a.) O grid é gerado dinâmicamente através dos dados recebidos do script grid.prg
b.) Quando eu recebo os dados eu vou criar uma coluna extra para inserir os botões de alteração.
c.) Quando é apenas um botão, tudo bem. Mas vou ter que criar outra coluna para inserir o botão de exclusão.
d.) Consequentemente eu vou ter que criar outra coluna extra, dinamicamente, para inserir os botões de exclusão.
e.) Então, o código do meu grid (brw.js) vai ficar cheia de tarefas que poderiam ser resolvidas antes, no script grid.prg.

Ou seja, vou adotar a seguinte estratégia :
1. A aplicação deve ser responsiva (celular/desktop/tablet/etc)
2. As alteraçoes/inclusões/deleções serão feitas via AJAX

Consequentemente devo evitar muito código javascript. Até porque eu não conheço muito.

**

Primeiro passo, no grid.prg

1.)

O JSON do grid é :

{
"Result":"OK",
"NextPage":true,
"PreviousPage":false,
"Header":["Nome","Cargo",""],
"Width":["60%","40%"],
"Rows":[
          {"___pk":7566,"ename":"JONES","job":"MANAGER"},
        {"___pk":7654,"ename":"MARTIN","job":"SALESMAN"},
        {"___pk":7698,"ename":"BLAKE","job":"MANAGER"}
      ]
}


Ele agora vai mudar para :

{"Result":"OK",
"NextPage":true,
"PreviousPage":false,
"Header":["Nome","Cargo","Alteração","Exclusão"],
  "Width":["60%","40%"],
"Rows":[
           {"___pk":7566,"ename":"JONES","job":"MANAGER"},
         {"___pk":7654,"ename":"MARTIN","job":"SALESMAN"},
         {"___pk":7698,"ename":"BLAKE","job":"MANAGER"}],
  "HasUpdateButton":true, <---- Novo campo (vai ter alteração?)
  "HasDeleteButton":true, <---- Novo campo (vai ter exclusão?)
  "HasInsertButton":true  <---- Novo campo (vai ter inclusão?)
  }


O código que faz isso está em grid.prg, vou reproduzir apenas as mudanças que eu fiz.
Eu aproveitei para organizar mais um pouco o código, para que ele fique mais claro.

Só alterei a parte final. Segue abaixo:
    /* Agora vou preparar o retorno no formato JSON. Primeiro o Hash hResult
       vai acumular os valores */
    hResult := { => }
   
    /*
     Vai ter botão de alteração, inclusão e de exclusão ?
     Geralmente essa resposta vem de alguma outra rotina, tipo aquela
     que define "níveis de acesso" ao usuário.
     Não vamos criar essa rotina agora. Por isso vou pressupor que todo grid
     terá tais botões. A seguir:
    */
    hResult[ "HasUpdateButton" ] := .t.
    hResult[ "HasDeleteButton" ] := .t.
    hResult[ "HasInsertButton" ] := .t.   
    /* ---------------- */
   
    hResult[ "Result" ] := "OK"
    hResult[ "NextPage" ] := lNextPage
    hResult[ "PreviousPage" ] := lPreviousPage
   
    /* Head (Nome das colunas) */
    AADD( aHead , "Nome" );AADD( aWidth , "50%" ) // 50%
    AADD( aHead , "Cargo" );AADD( aWidth , "30%" ) // 50% + 30% = 80%
    IF hResult[ "HasUpdateButton" ]
        AADD( aHead , "Alteração" );AADD( aWidth , "10%" ) // 80% + 10% = 90%
    ENDIF   
    IF hResult[ "HasDeleteButton" ]
        AADD( aHead , "Exclusão" );AADD( aWidth , "10%" ) // 90% + 10% = 100% (Largura total)
    ENDIF   
    hResult[ "Header" ] := aHead
    /* --------------------------------------------------- */
   
    hResult[ "Rows" ] := aReg
    hResult[ "Width" ] := aWidth
   
    /* Depois de concluído o processo, converto hResult em Json */
   
    ?? hb_JsonEncode( hResult )


*** Realizei várias alteraçoes no brw.js

Implementei os botões no grid já na chamada do AJAX
Ficou assim :

(1) Aqui eu recebo as definições dos botões lá do grid.prg via JSON
   /* Tem botões */
   if ( data.HasUpdateButton ){
      row += '<td><button type="button" class="btn btn-primary btn-sm btn-update" data-toggle="modal" data-target="#formModal">Alterar</button></td>';
   }
   if ( data.HasDeleteButton ){
      row += '<td><button type="button" class="btn btn-danger btn-sm btn-delete">Excluir</button></td>';
   }                     


(2) Aqui eu construo o evento do click que preenche o formulário. Mas esse código só pode ser chamado após o GRID construído (fora do laço)
   /* Se o botão de Update foi criado devo criar o evento ajax dele */
   if ( data.HasUpdateButton ){
      $($("#registros tbody tr").find(".btn-update")).click(function() {
           $.ajax({
                 type: 'POST',
                 url: "form.prg",
                 data: {
                      ___pk: $(this).closest("tr").attr('___pk') /* Mando a chave */
                  },
                 async: false,       
                 dataType: 'json',
                 success: function( data ){
                    console.log( data );
                  for ( let elem in data )
                     $( "#formModal" ).find( "#" + elem ).val( data[elem] ); /* Preencho os campos */

                 } 
           }); // ajax
       });
   }

****
Outras alterações foram feitas no código.
(1) Os eventos do botão de inserir foram retirados - $("#btnFormSubmit").click( function(){
(2) O método this.buttonUpdate() também foi retirado.

****
O clique do botão de exclusão ainda não foi feito, só o botão foi criado. Vou fazer isso amanhã.

2020-08-24_051516.png


Essa alteração foi um passo importante.

O código ficou mais simples, menor e mais fácil de manter.

Para o usuário, que ainda não viu mudanças, nada foi feito... Apenas um botão vermelho, que não serve para nada, foi criado.

ex18 - alteracao nos botoes.zip
(6.42 KiB) Baixado 94 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 24 Ago 2020 05:34

Esqueci de tirar o nome "Tutorial" da página.

Isso que estou fazendo são mais notas e exemplos. Talvez ajude a alguém. Mas não é tutorial. O tutorial acabou quando finalizei o grid. Essa etapa da criação dos formulários são mais notas de um estudante.

Tenho visto muitos vídeos sobre JQuery e muitos dizem que está acabando e que já terminou a sua era. Mas vou continuar com ela por enquanto. Ela é de fácil aprendizado e tem se atualizado. O problema é que ela é muito antiga (a sua origem) e tem muita coisa defasada na internet. Para quem não sabe javascript bem, o ideal é concentrar as tarefas no Harbour e aprender a usar a JQuery (com Ajax) + Bootstrap o suficiente para criar aplicações responsivas. O aprendizado de um framework não está descartado, mas não adianta partir para um Framework sem entender o básico. É um estudo que pode ser feito em paralelo e sem pressa. Não existem soluções mágicas.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 25 Ago 2020 07:40

Agora vou preparar a rotina de exclusão.

Criei um arquivo chamado delete.prg

Ele recebe a chave primária para a deleção e faz a operação.

  IF .NOT. hb_HHasKey( hPost , "___pk" )
       hRet["Result"] := "ERRO"
       hRet["Message"] := "A exclusão não pode ser realizada; A chave não foi enviada : " + hb_valtoexp(hPost)
       ?? hb_JsonEncode( hRet )
       RETURN
   ENDIF
   

  TEXT SQL TO cSQL
      DELETE FROM emp
      WHERE empno = :empno   
  ENDTEXT   
  cSQL := StrTran( cSQL , ":empno" , hPost["___pk"] )
   

   
   BEGIN SEQUENCE WITH __BreakBlock()
       oCn := win_oleCreateObject( "ADODB.Connection" )
       oCn:ConnectionString := cString
       oCn:Open()
       oCn:Execute( cSQL )
       oCn:Close()   
       hRet["Result"] := "OK"
       hRet["Message"] := "Operação realizada com sucesso : " + cSQL
   RECOVER USING oError
       hRet["Result"] := "ERRO"
       hRet["Message"] := oError:Description + " / " + cSQL
   END SEQUENCE   
   
   ?? hb_JsonEncode( hRet )


No cliente HTML o botão já tinha sido criado. Agora falta só criar o evento que chama o delete.prg

É semelhante ao evento que eu chamei na rotina de alteração.
if ( data.HasDeleteButton ){
   var gridThis = this; // Recebo o objeto grid
   $($("#registros tbody tr").find(".btn-delete")).click(function() {
       
        $.ajax({
              type: 'POST',
              url: "delete.prg",
              data: {
                   ___pk: $(this).closest("tr").attr('___pk') /* Mando a chave */
               },
              async: false, 
              context: this, 
              dataType: 'json',
              success: function( data ){
               if ( data.Result == "OK" ){
                  alert( "Exclusão realizada com sucesso!");
                  var tr = $(this).closest("tr");
                  console.log( tr );
                  tr.fadeOut(400, function() {
                     tr.remove();        
                     gridThis.refreshGrid(); // Refresh no objeto grid (contexto passado)
                  });
               } else {
                  alert( data.Message );
                  
               }   
                     
              } 
        }); // ajax
    });
}


Para testar escolha o funcionário "SCOTT ADAMS" porque os outros tem registros relacionas (FK) e não vão ser excluídos, mas uma mensagem de erro
vai retornar.

2020-08-21_013243.png


Vou mandar o projeto completo porque o CRUD está concluído.

ex19 - delete.zip
(824.32 KiB) Baixado 95 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 26 Ago 2020 10:55

Vamos agora iniciar uma nova etapa : a pesquisa de dados em outra tabela.

Caminho feliz :-)

1. O usuário "chega" em um campo de pesquisa
2. O usuário começa a digitar os dados (por ex: o nome do cliente)
3. O sistema abre uma pequena janela e vai dando o resultado da pesquisa
4. O usuário seleciona o valor
5. O sistema preenche o campo com o valor selecionado e grava o código em um campo oculto

**

A tarefa de hoje é criar esse campo de pesquisa usando os recursos do HTML5.
Não vamos fazer a rotina de pesquisa hoje. Vamos apenas entender como esse campo funciona.

**

Etapas

1. Primeiramente vamos dar uma olhada na estrutura da nossa tabela emp e ver se tem alguma chave estrangeira.

CREATE TABLE [emp] (
   [empno] Long PRIMARY KEY,
   [ename] VarChar(50),
   [job] VarChar(50),
   [mgr] Long,
   [hiredate] DateTime,
   [sal] Decimal(12, 2),
   [comm] Long,
   [deptno] Long,
   FOREIGN KEY ([deptno])
      REFERENCES [dept] ([deptno])
      ON UPDATE NO ACTION ON DELETE NO ACTION,
   FOREIGN KEY ([mgr])
      REFERENCES [emp] ([empno])
      ON UPDATE NO ACTION ON DELETE NO ACTION
)


O campo deptno informa o código do departamento do funcionário.
Vamos ver a tabela de departamento.

CREATE TABLE [dept] (
   [deptno] Long PRIMARY KEY,
   [dname] VarChar(50),
   [loc] VarChar(50)
)


Pronto, usaremos essa tabela lá na frente (não hoje).
Por enquanto vou usar os dados dessa tabela só para criar uma representação em HTML5 dos dados da pesquisa. Vou fazer uma pesquisa de "faz de conta", sem
realmente "pegar" os dados dessa tabela.

Os dados da tabela dept são :

DEPTNO  DNAME           LOC
------  -------------   -----------
10       ACCOUNTING       NEW YORK
20       RESEARCH       DALLAS
30       SALES           CHICAGO
40       OPERATIONS       BOSTON


Como seria a pesquisa desses dados em HTML5 ?

2. Recursos de pesquisa do HTML5.

Maiores detalhes em https://www.w3schools.com/tags/tag_datalist.asp

<input list="dept" name="dname" id="dname">

<datalist id="dept">
  <option value="ACCOUNTING">
  <option value="RESEARCH">
  <option value="SALES">
  <option value="OPERATIONS">
</datalist>


https://jsfiddle.net/vlademiro/ts2k5cb1/4/

3. Agora vamos incluir esse campo no formulário

   <form id="formData" action="#">
     <div class="form-group">
      <input type="hidden" name="___pk" id="___pk">
      <label for="ename">Nome:</label>
      <input type="text" class="form-control" name="ename" id="ename">
     </div>
     <div class="form-group">
      <label for="job">Cargo:</label>
      <input type="text" class="form-control" name="job" id="job">
     </div>
     <div class="form-group"> <------------------- NOVO CAMPO DENTRO DESSA DIV
      <label for="job">Departamento:</label>
      <input type="text" class="form-control" id="dname" list="dept">
     </div>
      <datalist id="dept">
        <option value="ACCOUNTING">
        <option value="RESEARCH">
        <option value="SALES">
        <option value="OPERATIONS">
       </datalist>
   </form>      
   
   


Atenção: note que eu não usei o atributo "name". A explicação detalhada a seguir :

a. Esse campo dname não será gravado no banco de dados. Eu apenas quero exibir.
b. Eu estou enviando os dados para a gravação através do método serialize(), que fica em $.ajax
c. O método serialize() manda apenas os campos com a tag "name" preenchida.
d. Ou seja, se o campo é apenas para exibição e não para gravação, não coloque a tag "name"

2020-08-21_100321.png
2020-08-21_100321.png (10.38 KiB) Visualizado 3617 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 27 Ago 2020 08:03

Mais um pequeno passo : pegar a lista do exemplo anterior e preencher com os dados do banco.

Caminho feliz

1. O usuário chega em um campo de pesquisa
2. O usuário começa a digitar os dados do que ele quer buscar
3. O sistema abre uma pequena janela e vai dando o resultado da pesquisa com dados do banco
4. O usuário seleciona o valor
5. O sistema preenche o campo com o valor selecionado e grava o código em um campo oculto

Etapas

1. Vamos criar um script chamado search.prg e retornar um JSON com a pesquisa

As pesquisas em banco de dados já foram abordadas, por isso vou botar somente o json de retorno.

{ "Result":"OK",
  "Rows":[
            {"dname":"ACCOUNTING"},
         {"dname":"OPERATIONS"},
         {"dname":"RESEARCH"},
         {"dname":"SALES"}
      ]
}


2. Agora vou preencher os dados com o JSON.

2.1. Primeiramente vou criar o local onde os dados coletados do JSON irão ficar :

<datalist id="dept"></datalist>


2.2. Agora vou criar um evento que irá ser ativado a cada tecla pressionada no campo de busca.

   $("#dname").keyup( function(e) {
      $.ajax( { type:"GET",
              url:"search.prg",
              data: {
                  word: $(this).val()
              },
              async: false,
              dataType: "json",
              context: this,
              success: function( data ){
                let datalist = "";
               switch ( data.Result ){
                case 'OK':
                  /***/
                  $(this).empty();
                  for ( let row of data.Rows ){
                     datalist += '<option value="'+ row.valueList +'">';
                  }
                  $("#dept").html( datalist );
                  console.log( datalist );
                  
                  break;
                case 'ERRO':         
                  alert( data.Message );
                  break;
               }   
              }, /* end success */
             error: function (xhr, ajaxOptions, thrownError){
                    alert( "Não consegui contactar com o servidor");
                  alert(xhr.statusText);
                  alert(thrownError);             
              } /* end error */
            } 
       ); /* end Ajax */
   }); /* end event keyup */


****
Conclusão

Ainda não terminamos. Temos a pesquisa com a lista vinda do banco de dados, mas a chave não retornou ainda.
O próximo passo é gravar a chave em um campo hidden para que ele possa ser gravado no banco de dados.
Anexos
search.prg
(2.18 KiB) Baixado 82 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Itamar M. Lins Jr. » 27 Ago 2020 22:16

Ola!
Tem uma questão que não estou lembrando.
Como o mod_harbour trata um ARRAY no método "post" da HTML ?
Usando o uHttpd, tem um tratamento fácil.
Quando fui para o APACHE eu coloquei o nome das variáveis começando com a letra "a" assim aXYZ para array.

Saudações,
Itamar M. Lins Jr.
Avatar de usuário

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 6927
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 309 vezes
Mens.Curtidas: 503 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 28 Ago 2020 02:40

Itamar M. Lins Jr. escreveu:Ola!
Tem uma questão que não estou lembrando.
Como o mod_harbour trata um ARRAY no método "post" da HTML ?


Vc precisa informar no formulário que é um array. Assim :
  <form action="index.prg" method="POST">
            <input type="number" name="meuArray[]" value="1">
            <input type="number" name="meuArray[]" value="2">
            <input type="number" name="meuArray[]" value="3">
            <input type="number" name="meuArray[]" value="4">
            <input type="submit">
   </form>


O modHarbour já vai entender que é um array.
local hPost := AP_PostPairs()

        ?? hb_ValToExp( hPost )


E vai imprimir :
{"meuArray"=>{"1", "2", "3", "4"}}
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 28 Ago 2020 02:44

O segredo está nos colchetes

Se esquecer dos colchetes o mod_harbour vai entender que é uma variável simples e vai armazenar o último valor :
{"meuArray"=>"4"}
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 28 Ago 2020 04:56

O desafio de hoje é salvar o código da descrição retornada.

Caminho feliz

1. O usuário chega em um campo de pesquisa (já feito)
2. O usuário começa a digitar os dados do que ele quer buscar (já feito)
3. O sistema abre um datalist e vai dando o resultado da pesquisa enquanto o usuário digita (já feito)
4. O usuário seleciona o valor (já feito)
5. O sistema preenche o campo com o valor selecionado (feito)
6. O sistema grava o código em um campo oculto <------------- Falta esse

Etapas

1. Conseguir o código a partir da descrição selecionada

Vou modificar o script search.prg para retornar o código

1.1. O select de busca vai exibir duas colunas. A primeira é o código e a segunda é a descrição (o que o usuário digitou)
TEXT SQL TO cSQL
 
   SELECT deptno, dname
      FROM dept
   WHERE dname LIKE '%:word%'   
   ORDER BY dname ASC

ENDTEXT


Durante a leitura preencho o JSON :
   /*
   0 = Código
   1 = Descricao
   */
   hReg[ "valueId" ] := oRs:Fields( 0 ):value
   hReg[ "valueDesc" ] := oRs:Fields( 1 ):value


O retorno terá essa aparência :
{ "Result":"OK",
  "Rows":[
             {"valueCod":10,"valueDesc":"ACCOUNTING"},
          {"valueCod":40,"valueDesc":"OPERATIONS"},
          {"valueCod":20,"valueDesc":"RESEARCH"},
          {"valueCod":30,"valueDesc":"SALES"}
       ]}


2. Gravar o código em um campo oculto (Hidden)

2.2. Primeiro vou criar o campo no formulário que receberá o código.

Esse campo será HIDDEN, mas como estou testando ele será visível.
<input type="text" class="form-control" name="deptno" id="deptno">

quando funcionar eu mudo para

<input type="hidden" class="form-control" name="deptno" id="deptno">


2.3. O meu datalist está no modelo padrão do HTML5

<option value="ACCOUNTING"></option>
<option value="OPERATIONS"></option>
<option value="RESEARCH"></option>
<option value="SALES"></option>


Vou ter que inserir nele a tag "cod" criada por mim para armazenar o código.

<option cod="10" value="ACCOUNTING"></option>
<option cod="40" value="OPERATIONS"></option>
<option cod="20" value="RESEARCH"></option>
<option cod="30" value="SALES"></option>


Para pegar o valor do código usei o evento focusout

   $("#dname").focusout( function() {
      $("#deptno").val( $("#dept").find("option").attr("cod") ); // <--- Gravo o valor selecionado (#dept) no campo oculto (#deptno)
   });


2020-08-21_175627.png
2020-08-21_175627.png (7.87 KiB) Visualizado 3588 vezes


Após o teste pode deixar o deptno oculto.

***********
Conclusões

Certa vez um colega meu, programador Delphi, me disse uma coisa sobre o MS Access :

"Eu até tentei programar em Access, ele é um excelente front-end, o problema é que
ele deixa as coisas 'espalhadas'"

Com isso ele quis dizer que a lógica fica distribuída em diversos eventos. Quem programa
em HMG também corre esse mesmo risco.

Esse fato, que aconteceu há uns 12 anos, me veio a memória enquanto escrevia o código Javascript.
O JQuery também deixa as coisas "espalhadas". Afinal ela é classificada como uma lib, não como um framework.

Mas o problema pode ser também porque estou aprendendo de novo e preciso isolar as partes para depois sintetizar em algo maior.

Esse meu estudo também está servindo para me aprofundar na nova programação web. Apesar de já
ter programado, nunca tinha usado o HTML5 nem o JQuery da forma como estou usando agora.
O Javascript também sofreu mudanças, está bem melhor do que há 15 anos. Muito melhor mesmo.

Também não descarto o estudo de algum framework, como o Vue, Angular ou React.
Mas por enquanto vou continuar com o JQuery. Ele está facilitando muito, apesar de suas falhas.
Não existe bala de prata.

Quanto a parte Harbour o código também está "espalhado".
Espero "juntar" essas partes em uma classe... ainda não sei bem. Já tem solução MVC para modHarbour,
talvez seja esse o caminho, mas a julgar pelo tanto de javascript que eu tenho visto nos
códigos de amostra do mod-harbour, eu acho é impossível trabalhar sem entender os conceitos básicos de javascript.

Por enquanto, no Harbour, tenho :

1. grid.prg : JSON do Grid
2. form.prg : JSON do conteúdo do formulário (usado para obter dados para alteração)
3. save.prg : Salva os dados (inclusão ou alteração)
4. delete.prg : Exclui um registro
5. search.prg : Busca e preenchimento de dados vindos de outra tabela

********

Voltando ao Javascript, na minha humilde opinião, um meio termo aceitável seria pegar uma função já pronta (algum plugin) para fazer a busca.
Portanto, vou descartar o código de busca feito. A lógica da busca (descrita no início) continua, mas tem que ter alguma coisa melhor em javascript (eu sei que tem, só não sei o que é).
É o assunto da próxima postagem.

************
Referências

https://www.it-swarm.dev/pt/javascript/executar-acao-ao-clicar-na-opcao-datalist-html5/1052102244/
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 29 Ago 2020 01:47

Achei a pesquisa muito trabalhosa. Vou obter uma ajuda extra através de algum código já pronto.

JQuery tem muitos plugins disponíveis. Uma busca simples no Google, tipo "jquery plugin select" já me retorna muita coisa.

Achei esse aqui : jqueryui

Pontos negativos

(1) A última versão é de setembro de 2016. O repositório recebeu atualizações até 2018. https://github.com/jquery/jqueryui.com

Pontos positivos
(1) Possui uma página oficial com vários exemplos : https://jqueryui.com/
(2) É a lib oficial do jquery, mantida pela mesma equipe.
(3) A instalação é fácil.
(4) Tem muita coisa no site stackoverflow e em vários blogs.
(5) Tem muita video aula no youtube
(6) O seu aprendizado não exige mão-de-obra especializada, como os Frameworks.

Alguns exemplos no site oficial : https://jqueryui.com/autocomplete/

Para instalar apenas informe o caminho depois de ter "chamado" a lib jquery.

   <script src="js/jquery-ui-1.12.1/jquery-ui.js"></script>


O formulário teve que mudar. Não vou usar mais a data-list do html5, vou usar um controle de texto simples para receber a lista
e o controle hidden para receber o código vai permanecer.
 <div class="form-group">
   <label for="dname">Departamento:</label>
   <input type="hidden" class="form-control" name="deptno" id="deptno"> <------ O código retornado ficará aqui
   <input type="text" class="form-control" id="dname"> <------ O local onde o usuário vai digitar é aqui
  </div>


Para fazer o nosso autocomplete associe o campo onde o usuário vai digitar com a função de autocomplete.
$( document ).ready( function(){
   $('#dname').autocomplete({
         minLength: 1, <--- Mínimo de caracteres para começar a pesquisar
         autoFocus: true, <--- O primeiro elemento será selecionado assim que o menu aparecer
         delay: 300, <--- Espera (milisegundos) entre uma consulta e outra / Se ficar lento aumente o valor
         source: function(request, response){ <-- A lista é montada nessa função
            $.ajax({
               url: 'search.prg', <--- função de busca
               type: 'get', <--- tipo de requisição
               dataType: 'json', <--- dado de retorno
               data: {
                  'word': request.term <--- o que o usuário digitou eu mando
               },
               success: function(data){
                  if(data.Rows.length > 0)
                     response( data.Rows ); <--- elementos da lista
               }
            });
         },
         select: function(event, ui) { <---- Aqui é a quando o usuário seleciona
            $("#dname").val(ui.item.label);// preenche o campo com a descrição
            $("#deptno").val(ui.item.value); // preenche o código
            return false; // não sei porque é falso
         }   
      });


Ficou bem mais claro o código e bem menor também. Todo o código de controle ficou em apenas um ponto.
Se ficar lento devido as condições da rede altere o minLenght para um valor maior do que 1 e o delay para um valor maior do que 500.

2020-08-28_231007.png
2020-08-28_231007.png (8.68 KiB) Visualizado 3568 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 30 Ago 2020 04:21

Como encerrei a fase do autocomplete estou enviando o zip.

Estou evitando mandar zips por causa do tamanho. Eles até que não são muito grandes, mas vão ocupar espaço na hospedagem se eu mandar a cada atualização.
Estou anexando alguns de vez em quando. Sempre que termino uma fase.

Esse que estou anexando tem :

1. Banco de testes
2. index.html
3. todos os prgs
4. Biblioteca bootstrap + jquery (estão mimificados, mas mesmo assim ocupam espaço)

Está dando uns 700Kb por zip.

Assim, é só descompactar e testar. Precisa só do modHarbour e do ODBC 64bits instalado.

Uma outra coisa importante que gostaria de compartilhar :
O foco é Harbour mas não tem como fugir de detalhes de HTML , etc.
A biblioteca JQuery está caindo em desuso e a JQUERYUI também tem recebido poucas atualizações.
Contudo, esse não é o foco. O foco continua sendo Harbour. Os princípios aqui abordados servem para você integrar
com qualquer outra solução javascript que suporte Json.

Estou evitando usar os recursos gráficos da JQueryUI. Estou apenas usando o controle autocomplete e estilizando com o Bootstrap, assim não bagunça os estilos.

Existem soluções em Javascript que pegam um Json e transforma em formulário ou grid.

https://js.devexpress.com/Demos/WidgetsGallery/
https://datatables.net/examples/index
http://www.alpacajs.org/
https://jsonforms.io/

Alguns são licenciados, alguns são open-source e outros livres para uso não comercial. Veja com atenção.
Anexos
ex24 - jqueryUI - autocomplete.zip
(714.68 KiB) Baixado 80 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 749
Data de registro: 11 Jul 2005 02:46
Curtiu: 22 vezes
Mens.Curtidas: 62 vezes

Anterior Próximo



Retornar para Contribuições, Dicas e Tutoriais

Quem está online

Usuários vendo este fórum: Nenhum usuário registrado online e 15 visitantes


Ola Amigo, espero que meu site e forum tem lhe beneficiado, com exemplos e dicas de programacao.
Entao divulgue o link da Doacao abaixo para seus amigos e redes sociais ou faça uma doacao para o site forum...
MUITO OBRIGADO PELA SUA DOACAO!
Faça uma doação para o forum
cron
v
Olá visitante, seja bem-vindo ao Fórum Clipper On Line!
Efetue o seu login ou faça o seu Registro