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 » 14 Ago 2020 19:32

Paginando o grid

A paginação do grid é feita em duas etapas.

A primeira delas é na geração do JSON, em dados.prg

(1) Recebo do navegador a página para onde devo ir
(2) Verifico se tal página existe, se não existir retorno uma mensagem de erro
(3) Se existir carrego os dados da página

Nessa etapa farei apenas o dados.prg

Na próxima etapa faremos o javascript.

Vou assumir que o tamanho da página é 3 registros. Esse valor vai ficar fixo em dados.prg

Vamos por partes.

Para receber um valor get use AP_GetPairs, ele retorna um HASH com os valores enviados através do método GET do navegador

   LOCAL hGet := AP_GetPairs()
   LOCAL nPagina := VAL( hb_HGetDef( hGet , "pagina" , "1" )) // Se não informar a página recebe 1 (primeira página)


cabget.png
cabget.png (839 Bytes) Visualizado 422 vezes


Agora é só adaptar a página enviada ao comando SQL. Criei duas tags: ":registroPorPagina" e ":offset" para poder substituir com StrTran.

":registroPorPagina" vou trocar pelo número de registro por página (fixei em 3)
":offset" é o ponto onde vou começar a contar (se a partir do primeiro, do décimo, etc)

Infelizmente o MS Access é muito fraco para paginar, mas o SQL Server, PostgreSQL, MySQL , etc
tem comandos bem simples. O caso do MS Access ficou tão chato que eu tive que recorrer a um comando que eu criei dias atrás para facilitar a exibição
do SQL.

Enfim, no MS Access ficou assim :
        SELECT TOP :registroPorPagina tb01.*
            FROM emp AS tb01
            LEFT JOIN (
                SELECT TOP :offset empno FROM emp ORDER BY empno ASC
            ) AS tb02
            ON tb01.empno = tb02.empno
        WHERE ISNULL(tb02.empno)
        ORDER BY tb01.empno ASC



Se fosse MySQL/PostgreSQL, por exemplo, bastaria o seguinte :
"SELECT * FROM emp LIMIT :registroPorPagina OFFSET :offset


Bem, agora é só realizar as substituições na Query
   cSQLReturn := StrTran( cSQL , ":registroPorPagina" , hb_ntos( nRegistroPorPagina ) )
   cSQLReturn := StrTran( cSQLReturn , ":offset" , hb_ntos( nRegistroPorPagina * ( nPagina  )  ) )


Já temos os dados da página atual, mas falta saber se existe a página seguinte e a anterior para habilitar os botões quando chegarmos no Javascript.
Criei duas variáveis lógicas (lNextPage e lPreviousPage) para retornar em formato JSON.

para saber se tem a próxima página o jeito é repetir a query com nPagina+1 (se o reccount do ADO voltar zero então não tem, e lNextPage é falso)
para saber se tem a página anterior é só verificar se nPagina-1 é <= 0 (se for então não tem página e lPreviousPage é falso)

cabget.png
cabget.png (839 Bytes) Visualizado 422 vezes
Anexos
pagina.png
pagina.png (4.66 KiB) Visualizado 422 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 14 Ago 2020 19:34

O código que faz a paginação ficou assim :

#define adOpenForwardOnly 0
#define adOpenKeyset 1
#define adOpenDynamic 2
#define adOpenStatic 3
#define adLockReadOnly 1
#define adLockPessimistic 2
#define adLockOptimistic 3
#define adLockBatchOptimistic 4
#define adUseNone 1
#define adUseServer 2
#define adUseClient 3
#define adStateClose

#xtranslate TEXT SQL TO <v>  => #pragma __text|<v>+=%s;<v>:=""

function main

   LOCAL oRs , oCn, cSql, cSQLReturn, cSQLNext, lNextPage := .t. , lPreviousPage := .t.
   LOCAL cString := "Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=" + PathBase() + "/scott.mdb"
   LOCAL hReg
   LOCAL aReg := {}
   LOCAL hGet := AP_GetPairs()
   LOCAL nPagina := VAL( hb_HGetDef( hGet , "pagina" , "1" )) // Se não informar a página recebe 1 (primeira página)
   LOCAL nRegistroPorPagina := 3
   

   oCn := win_oleCreateObject( "ADODB.Connection" )
   oCn:ConnectionString := cString
   oCn:Open()
   
   oRs := win_oleCreateObject( "ADODB.Recordset" )
   oRs:CursorLocation = adUseClient
   
   
   TEXT SQL TO cSQL
     
        SELECT TOP :registroPorPagina tb01.*
            FROM emp AS tb01
            LEFT JOIN (
                SELECT TOP :offset empno FROM emp ORDER BY empno ASC
            ) AS tb02
            ON tb01.empno = tb02.empno
        WHERE ISNULL(tb02.empno)
        ORDER BY tb01.empno ASC
   
   ENDTEXT
   
   cSQLReturn := StrTran( cSQL , ":registroPorPagina" , hb_ntos( nRegistroPorPagina ) )
   cSQLReturn := StrTran( cSQLReturn , ":offset" , hb_ntos( nRegistroPorPagina * ( nPagina  )  ) )
   
 
   oRs:Open( cSQLReturn , oCn , adOpenDynamic, adLockOptimistic )

   if oRs:recordcount > 0
        oRs:Movefirst()
        do while !oRs:eof()
            hReg := {=>}
            hReg[ "id" ] := oRs:fields("empno"):value
            hReg[ "nome" ] := oRs:fields("ename"):value
            AADD( aReg , hReg )
            oRs:movenext()
        enddo
        oRs:Movefirst() // Opcional, retorna para o primeiro registro
    endif   
    oRs:Close()
   

   // A próxima página existe ?

   cSQLNext := StrTran( cSQL , ":registroPorPagina" , hb_ntos( nRegistroPorPagina ) )
   cSQLNext := StrTran( cSQLNext , ":offset" , hb_ntos( nRegistroPorPagina * ( nPagina+1  )  ) )
   oRs:Open( cSQLNext , oCn , adOpenDynamic, adLockOptimistic )
   if oRs:recordcount == 0
       lNextPage := .f.
    endif   
   // A página anterior existe ?
   if nPagina-1 <= 0
       lPreviousPage := .f.
    endif
   
   
    ?? hb_JsonEncode( { "Result" => "OK" , "NextPage" => lNextPage , "PreviousPage" => lPreviousPage , "Rows" => aReg } )

return nil

/**
https://stackoverflow.com/questions/8627032/ms-access-limit-x-y
https://php.developreference.com/article/25294673/MS+Access+LIMIT+X%2C+Y

SELECT TOP PageItemsCount tb01.*
FROM myTable AS tb01
LEFT JOIN (
SELECT TOP OffsetValue ID FROM myTable ORDER BY ID ASC
) AS tb02
ON tb01.ID = tb02.ID
WHERE ISNULL(tb02.ID)
ORDER BY tb01.ID ASC

SELECT Person.*
FROM Person
WHERE Person.Id In
      (
        SELECT TOP 10 A.Id
        FROM [
               SELECT TOP 30 Person.Name, Person.Id
               FROM Person
               ORDER BY Person.Name, Person.Id
             ]. AS A
        ORDER BY A.Name DESC, A.Id DESC
      )
ORDER BY Person.Name, Person.Id;
*/

Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 14 Ago 2020 19:41

O código Javascript

Agora a segunda etapa.

Criei uma biblioteca brw.js para poder manusear o meu browser (grid) e coloquei como anexo.
Ela é apenas uma classe com 4 métodos, sendo que o principal deles nós já vimos.

Apenas vou acrescentar aqui que, como eu fiz algumas alterações na estrutura do JSON, eu vou ter que modificar na hora de receber os dados.
Antes eu tinha apenas os registros retornando, agora eu tenho a página anterior, a página posterior e os registros.

Terei que substituir a linha que faz a leitura

de
$.each( data, function( key, val ) {


por
$.each( data.Rows, function( key, val ) {


Não vou comentar a classe aqui, mas ele é bem simples mesmo. É melhor dominar uma classe simples
do que ficar baixando muitos Kbytes de bibliotecas de grid que nós não dominamos. Já fomos orfãos do Clipper,
não vamos correr tantos riscos assim.

Depois que vc dominar esse grid e sentir necessidade de algo melhor, faça uma busca no Google por "grid javascript json"

2020-08-14_191336.png


Esse modelo eu considero fácil de customizar. Além disso o bootstrap é responsivo, ele se ajusta a uma tela de smartphone facilmente.

A parte do harbour foi fácil, agora é estudar bootstrap, testar e pesquisar outras soluções em javascript. Não é nenhum bixo de sete cabeças, já foi bem mais difícil.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 14 Ago 2020 19:42

Os arquivos usados na segunda parte :

ex07_11.zip
(359.03 KiB) Baixado 12 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 14 Ago 2020 19:49

Alguns comentários :

* A vantagem de se trabalhar com JSON é a vantagem que o Javascript tem de reconhecê-lo e interpretá-lo.

Quando o mod_harbour retorna um JSON, o Javascript já interpreta como um Hash dele e já podemos trabalhar sem ter que "receber e converter" os dados.

JSON = "JavaScript Object Notation"

* Procure trabalhar com o bootstrap. Ele facilita muito, se adapta ao celular facilmente e tem muitos exemplos prontos.

* Use ajax. No inicio pode parecer chato, mas no final o código fica mais fácil de ler e manter. Note que o código ajax permite isolar o código feito em harbour e acaba com a mistura de código com html. Essa mistura só é bom para pequenos projetos e para a gente ir aprendendo, mas pode até ser prejudicial pois pode criar mal hábitos de programação. Depois o cliente começa a pedir coisas e mais coisas e o código simples fica uma salada.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Itamar M. Lins Jr. » 17 Ago 2020 16:45

Ola!
Muito bom. Parabéns!
Algum SGBD já retorna o RS(Result SET) em JSON ?

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

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 4628
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 161 vezes
Mens.Curtidas: 254 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 17 Ago 2020 20:35

Eu não testei, mas o postgres tem funções para isso. Vou ver se acho alguma coisa.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 17 Ago 2020 20:36

Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 17 Ago 2020 20:42

Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Itamar M. Lins Jr. » 17 Ago 2020 21:34

Ola!
Muito bom, ponto para o PG!
Só isso já poupa "DO WHILE" pq o que não quero é ficar refazendo "DO WHILE" para recriar sentença.
MS_SQL:
https://docs.microsoft.com/en-us/sql/relational-databases/json/format-query-results-as-json-with-for-json-sql-server?view=sql-server-ver15
MariaDb:
https://mariadb.com/kb/en/json-functions/

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

Itamar M. Lins Jr.
Colaborador

Colaborador
 
Mensagens: 4628
Data de registro: 30 Mai 2007 11:31
Cidade/Estado: Ilheus Bahia
Curtiu: 161 vezes
Mens.Curtidas: 254 vezes

Mini tutorial mod_harbour

Mensagempor Ranier » 18 Ago 2020 09:44

Itamar M. Lins Jr. escreveu:Ola!
Muito bom, ponto para o PG!
Só isso já poupa "DO WHILE" pq o que não quero é ficar refazendo "DO WHILE" para recriar sentença.

Olá Itamar,
O PostgreSQL têm suporte muito bom para o JSON:
https://www.postgresql.org/docs/12/datatype-json.html
Alías um dos pontos fortes do PostgreSQL é a documentação (em inglês), é bem completa, e muito trabalhada,
inclusive no código fonte, eles fazem questão de manter tudo bem atualizado e em ordem.

https://www.postgresql.org/docs/
Ranier
Usuário Nível 2

Usuário Nível 2
 
Mensagens: 79
Data de registro: 02 Abr 2019 09:01
Cidade/Estado: Goiania/Goias
Curtiu: 0 vez
Mens.Curtidas: 9 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 18 Ago 2020 14:34

O mini-tutorial acabou.
Durante o tutorial, as técnicas usadas realmente funcionam. É feijão com arroz.
Muitos projetos para web usam o grid ajax com paginação. Pode não ser exatamente como eu fiz mas é na mesma linha.
É a mesma novela se repetindo com personagens diferentes.
O enredo da novela tem HTML5, CSS, JAVASCRIPT e JSON. Os personagens que mudam a cada versão da mesma novela chamam-se : PHP, ASP,
PYTHON, JAVA, PERL, CGI e agora modHarbour.

Depois que esses conceitos ficarem claros é que vale a pena passar para um framework, na minha opinião. E depois, quem sabe, criar o seu framework.

O último arquivo zip postado e dentro dele (pasta ex11) tem o código completo do grid.
Quem quiser pode usar como modelo para criar os seus.

** **********************

A partir desse ponto começa uma outra fase.
Vou postar como estou fazendo para linkar formulários de inclusão / alteração a partir do grid. Não é um tutorial, é mais a minha experiência mesmo...

**

Bem, o objetivo dessa etapa é receber a chave primária para conectar o grid ao futuro formulário.
Existem diversas formas de fazer isso, a forma que eu escolhi fazer é a descrita abaixo :

(1) Primeiro uma revisão. O arquivo dados.prg retorna um JSON no formato :

{  "Result":"OK",
   "NextPage":true,
   "PreviousPage":false,
   "Rows":[{"id":7566,"nome":"JONES"},{"id":7654,"nome":"MARTIN"},{"id":7698,"nome":"BLAKE"}]
}


(2) O objetivo é armazenar a chave primária no grid, e usar essa chave para abrir um futuro formulário (a ser criado).
(3) Problema : como o sistema (lado cliente html) vai saber qual é a chave primária para ele usá-la para abrir o formulário ? Como o servidor (mod_harbour) vai dizer ao cliente qual é a chave ?
(4) A solução que eu adotei foi : criar um campo especial e armazenar nele a chave primária. Esse campo deve ter um nome bem diferente para evitar problemas futuros.

Vou chamar esse campo de "__pk".

Fiz uma pequena modificação no dados.prg para poder retornar esse campo.

O Json acrescentei __pk ao retorno do json

{"Result":"OK",
"NextPage":true,
"PreviousPage":false,
"Rows":[{"___pk":7566,"id":7566,"nome":"JONES"},{"___pk":7654,"id":7654,"nome":"MARTIN"},{"___pk":7698,"id":7698,"nome":"BLAKE"}]}


Tive que modificar também o brw.js para armazenar esse valor.
Mas onde armazenar ?

Eu resolvi criar um atributo (presente em cada linha do grid) e armazenar nesse atributo o valor.
O nome do atributo que eu criei é "__pk" também.

Fiz uma pequena modificação no brw.js para receber esse valor e armazenar na nova tag que eu criei.
Isso é feito dentro do $.ajax, na "função de usuário" usada na recepção dos dados.

Mais precisamente aqui :
   $('<tr ___pk=' + val.___pk + '>').html( "<td>" + val.id + "</td>" +
                  <td>" + val.nome + "</td>" ).appendTo("#registros tbody");


O HTML é a representação de uma estrutura de dados bastante conhecida: uma árvore.
Essa árvore recebeu, dos seus criadores, o nome de DOM (Document Object Model).
O Javascript é a linguagem que permite navegar por essa complexa estrutura. Com ele
eu crio elementos, apago elementos e posso até criar meus próprios atributos para uso particular.
O JQuery torna o Javascript mais fácil. É uma boa porta de entrada.

Cada linha da tabela que era assim :
<tr> <td> ... </td> etc.. </tr>


Vai ficar assim :
<tr ___pk='valor da chave primária'> <td> ... </td> etc.. </tr>


O contexto é esse :

 success: function( data ){
                    $("#anterior").attr("disabled", !data.PreviousPage );
                    $("#proximo").attr("disabled", !data.NextPage );
               switch ( data.Result ){
                     case 'OK':       
                        $.each( data.Rows  , function( key, val ) {
                     
                     $('<tr ___pk=' + val.___pk + '>').html( "<td>" + val.id + "</td>" +
                              "<td>" + val.nome + "</td>" ).appendTo("#registros tbody");
                        }); 
                        break;
                  }   
              } 


Agora vou criar os botões para quando o usuário clicar nele chame o formulário.

Não vou criar o formulário agora. Vou só criar um alert para ter certeza de que o valor
da chave primária está chegando corretamente.

   // Crio a coluna extra e dentro dele coloco um botão (vai ficar um botão por linha)
    $("#registros tbody tr").each(function() {
      $(this).append('<td><button type="button" class="btn btn-primary btn-sm">Alterar</button></td>');
     });
   // Crio eventos para quando o usuário clicar em cada botão criado   
    $("#registros tbody tr td button").click(function() {
        alert( $(this).closest("tr").attr('___pk') ); // <--------------- aqui o alert (no futuro será o formulário)
     });


2020-08-17_123046.png


Em resumo :

1. O tutorial acabou na postagem anterior.
2. Daqui para frente é como estou fazendo para ligar o registro ao browser e como realizar gravações no banco (o CRUD).
3. Os códigos são para exemplo. Não use em projetos práticos. Tem trechos que podem ser otimizados, redundâncias, etc.
4. O objetivo é ajudar. Como sempre, precisa ter o mod_harbour instalado com xampp em ambiente windows. Descompacte os zips de exemplo dentro de htdocs.
5. Como sempre, não vou me aprofundar em Javascript/HTML/Bootstrap, mas não tem como fugir dessas tecnologias. As explicações nesses tópicos são rápidas e não se aprofundam.

Segue o fonte :
ex12 - armazenando a chave.zip
(108.39 KiB) Baixado 8 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 19 Ago 2020 22:10

Nessa etapa eu criei o atributo width para regular a largura das colunas.
Vou procurar também esclarecer alguns pontos do código javascript.

P. Como vou definir a largura das colunas ?
R. Vou mandar esse dado através do harbour via JSON.

P. Por que é necessário fixar a largura das colunas ?
R. Quando passava de uma página para outra a largura das colunas estava variando ligeiramente.
Faça um teste com os exemplos anteriores para comprovar.

Portanto, vou corrigir esse pequeno problema e ao mesmo tempo rever alguns pontos do javascript, apesar
desse não ser o foco do estudo.

O objetivo principal, portanto, é criar uma propriedade width lá no modHarbour (dados.prg) e enviar junto com os outros dados.

Em dados.prg ficou assim :

    // Head
    AADD( aHead , "Código" );AADD( aWidth , "20%" ) // <--- aWidth armazena string com largura relativa
    AADD( aHead , "Nome" );AADD( aWidth , "70%" ) // <---- Não vai somar 100% por causa dos botões que vão ocupar espaço, é bom deixar uma sobra.



O JSON do modHarbour agora ficou assim :
{"Result":"OK", <--------- Deu certo! Não estou tratando outros casos, mas deixo aqui para posterior mudança.
"NextPage":true, <------- Tem próxima página ? (para habilitar/desabilitar) botões
"PreviousPage":false, <-- Tem página anterior ?
"Header":["Código","Nome",""], <---- O cabeçalho (tem um extra vazio para a coluna dos botões de alteração)
"Width":["20%","70%"], <------------ O tamanho das colunas (a alteração dessa aula)
"Rows":[{"___pk":7566,"id":7566,"nome":"JONES"}, <---- Os dados + valor da chave primária (___pk)
         {"___pk":7654,"id":7654,"nome":"MARTIN"},<---+
       {"___pk":7698,"id":7698,"nome":"BLAKE"}] <---+
}


Agora vamos parte onde recebo os dados :
No navegador eu recebo esses dados juntamente com as definições do grid. Nada vai mudar, apenas vou referenciar o que fiz em dados.prg no javascript.

Vou repetir o que já coloquei em tópicos anteriores. São pequenas observações sobre o código javascript:
1. O JQuery/Javascript tem funções que recebem automaticamente tudo o que mando pelo modHarbour sem precisar converter nada. O padrão JSON garante isso.
2. Esse recebimento é feito por $.ajax
3. Essa "função de usuário" (lembre-se da DBEDIT) recebe um parâmetro data (ou o nome que eu quiser). Ela será chamada somente se o navegador conseguir acessar a sua página --> success: function( data )
4. Esse parâmetro data [ em function( data ) ] é um hash javascript com o conteúdo do json que eu enviei do harbour.

Você pode achar estranho a notação function( data ), mas é porque o Javascript aceita funções sem nome.
Pense nelas como blocos de código extendidos.

Nesse trecho abaixo eu uso data.Width para preencher a propriedade width de cada coluna (td) da minha tabela.

for ( let elem in val ){
      if (elem != "___pk")
         row += '<td width="'+ data.Width[++ord] +'">' + val[elem] + '</td>';
   }


O contexto do trecho acima é a função $.ajax, que fica no método RefreshGrid, em brw.js :
Vou reproduzir o contexto abaixo e comentar alguns trechos.

   RefreshGrid( nSkip = 0 ){  <--- Atualização do grid (ele é chamado a cada passada de página. 
     
      $("#page").text( parseInt( $("#page").text() )  + nSkip);     <---- Atualizo o número da página para o usuário
     $("#registros thead").empty();  <---- Limpo os cabeçalhos 
      $("#registros tbody").empty();  <---- Limpo os dados (agora a tabela ficou vazia  (a partir da linha de baixo vou começar a preencher de novo)
      $.ajax({ <---------------------------- Começo a consulta ao modHarbour
              type: 'GET',  <--------------- Método que vou usar.
                                          O modHarbour vai receber os dados através do GET (não do POST)
                                 
              url: "dados.prg", <----------- Os script modHarbour que vai receber os dados e retornar o resultado
          
              data: { <--------------------- Aqui é o que estou mandando
                     pagina: $("#page").text() <----- Só estou mandando um parâmetro (a página)
               },
              async: false, 
              dataType: 'json',
              success: function( data ){  <- Minha "função de usuário" ( data é o JSON de retorno com o que o modHarbour vai me retornar, aqui ele já "virou" hash javascript)
                let head = "";
               let row;
               let ord;
             
                    $("#anterior").attr("disabled", !data.PreviousPage );
                    $("#proximo").attr("disabled", !data.NextPage );
               switch ( data.Result ){
                     case 'OK':   
                   console.log( data ); <-------- Aqui é o meu "depurador" / Equivale ao alert para ver o conteúdo de uma variável que vc tem dúvida
                   head += '<tr>';
                  for ( let elem of data.Header ){
                     head += '<th>' + elem + '</th>';
                   }
                  head += '</tr>';
                  $( head ).appendTo("#registros thead");

                        for ( let val of data.Rows ){
                     if ( typeof val.___pk != "undefined" ) <----- Se tiver chave primária eu preciso guardar ela
                        row = '<tr ___pk=' + val.___pk + '>';
                     else
                        row = '<tr>'; <-------------------- Como não veio chave primária não guardo
                     ord = -1 <-------------- Posso fazer melhor do que isso :-)
                     console.log( data.Width );  <-------- Meu "depurador" de novo
                     for ( let elem in val ){
                        if (elem != "___pk")
                           row += '<td width="'+ data.Width[++ord] +'">' + val[elem] + '</td>';
                     }
                     row += "</tr>";
                     console.log( row ); <------- A linha que estou retornando. (Mais depuração / Não esqueça de apagar depois
                     $( row ).appendTo("#registros tbody"); <--------- Finalmente adiciono as linhas ao tbody do grid #registros (esse nome não era para estar fixo aqui dentro da minha função)
                  }   
                  
                       
                        break;
                  }   
              } 
      }); // ajax
  }


Pare quem nunca viu, o console.log() é uma função especial do Javascript só para servir de depuração.
Pense naqueles avisos ? e ?? que a gente bota dentro do código quando tem alguma dúvida.

Para ver o conteúdo de console.log você precisa ativar o console do seu navegador. No Chrome basta clicar Ctrl+Shift+I e selecionar a aba Console.

Entendo o desconforto de quem nunca viu esses códigos. Mas analisando o arquivo brw.js e index.html dá para ter uma noção melhor.

* VOLTANDO AO ASSUNTO DA LARGURA

Essas larguras são das colunas. A função dela é evitar que elas mudem de tamanho ligeiramente
a cada passada de página.

A largura das colunas é uma coisa, a largura do GRID é outra. A largura do GRID só vai variar se o usuário redimensionar a janela, se acessar
o sistema de um smartphone, tablet, etc.

Em resumo: A largura total do GRID é para permanecer a mesma quando houver uma mudança na página dos registros (botão next/previous).

A princípio você não precisa mecher na largura do GRID, mas se precisar alterar, faça nesse trecho do HTML :

    <div class="row">
     <div class="col-md-12">   <-------------- altere 12 para outro valor inferior (12 é o máximo)
        <table id="registros" class="table table-striped">
              <thead></thead>
              <!-- Dados -->
              <tbody>
              </tbody>
              <!-- Dados -->
        </table>
     </div>    
   </div>


Sei que para quem tem pouca experiência é difícil, mas nesse caso da largura do GRID, tudo
o que você tem a fazer é mudar "col-md-12" para "col-md-10", por exemplo.
Você pode ter vários col dentro de um row, mas a soma dos col não pode passar de 12.

Exemplos :

<div class="row"> <!-- 4 + 8 = 12 -->
    <div class="col-md-4"> </div>
    <div class="col-md-8"> </div>
</div>
<div class="row"> <!-- 3 + 6 + 3 = 12 -->
    <div class="col-md-3"> </div>
    <div class="col-md-6"> </div>
    <div class="col-md-3"> </div>
</div>
<div class="row"> <!-- 10 + 2 = 12 -->
    <div class="col-md-10"> </div>
    <div class="col-md-2"> </div>
</div>


Esse número 12 é uma espécie de largura relativa. Como o sistema vai ter que se adaptar a qualquer tela, eu não posso trabalhar com números absolutos.

Quem quiser saber mais, pesquisa "sistema de grids bootstrap".

O valor máximo é 12, você pode ir diminuindo e dando refresh no navegador (Ctrl+F5).

Nota: O refresh real de um navegador é Ctrl+F5, não somente F5. Ctrl+F5 força a leitura total e não usa o que ficou armazenado no cache do navegador.

Espero não ter complicado mais do que ajudado. Não espere entender tudo de primeira.

ex13 - width colunas.zip
(87.67 KiB) Baixado 10 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 20 Ago 2020 11:31

Mais um pequeno passo: vou trocar o alert por um form modal usando bootstrap.
Só o form modal. Limpo. Sem os campos. Apenas troquei o alert pelo form.

O form modal vai exibir a chave primária.

* Criando o form modal

O form modal eu copiei pronto do site do bootstrap. É um exemplo padrão.

<!-- Trigger the modal with a button -->
<!-- Modal -->
    <div class="modal fade" id="formModal" tabindex="-1" role="dialog" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h4 class="modal-title">Meu Formulário</h4>
                </div>
                <div class="modal-body">
                    <div id="content"></div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">Fechar</button>
                    <button type="button" class="btn btn-primary">Ok</button>
                </div>
            </div>
        </div>
    </div>
<!-- Acabou o form aqui -->   


Antes de mostrar o código um pouco de teoria :

* O form é chamado através de um EVENTO.

P. O que é um evento ?
R. Um evento é uma REAÇÃO a uma determinada AÇÃO em um OBJETO.

Por exemplo :

1. Ao clicar (AÇÃO)
2. em um botão (OBJETO)
3. abra a janela chamada formModal (REAÇÃO)

No exemplo simples abaixo :
1. Ao clicar (click/AÇÃO) no botão #meuBotao (OBJETO)
2. Exiba o alert (REAÇÃO)

Para criar uma ação apenas um objeto simples (só um exemplo) :
 $("#meuBotao").click(function() {
         alert( 'Ola' );
       });


* A Biblioteca Bootstrap (com a ajuda do JQuery) facilita ainda mais. Ela já possui uma janela modal prontinha.
Mas você precisa informar os 3 passos anteriores.

Abaixo só tem DUAS ETAPAS, mas os 3 PASSOS VISTOS ACIMA estão implícitos no processo.

Vamos lá :

*** PARTE 1 : Identifique a janela. Vou chamá-la de "formModal"

 <div class="modal fade" id="formModal" tabindex="-1" role="dialog" aria-hidden="true">


*** PARTE 2 : Identifique o objeto botão. Note que o Bootstrap já tem uma tag para informar a REAÇÃO ao clique.

O botão que chama o formulário tem o atributo data-target (do bootstrap). Esse atributo deve referenciar o ID ("formModal") :
<td><button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#formModal">Alterar</button></td>


Esse código acima eu embuti no brw.js (não se preocupe com isso por enquanto, apenas entenda o processo geral, porque ele
é o mesmo para outras bibliotecas e frameworks).

* PRONTO: A conexão e a abertura do form é o bootstrap que faz.

Mais uma vez: não precisa entender tudo de imediato. Isso é impossível. Mas veja que a lógica usada é uma velha conhecida nossa.

2020-08-17_130959.png
2020-08-17_130959.png (9.95 KiB) Visualizado 299 vezes


*** PARTE 3 : Ficou faltando entender uma coisa... Como a chave primária foi parar lá dentro ?

R. Eu criei um outro evento para cada botão do grid. Assim, cada botão tem dois eventos :

(1) O evento criado acima pelo bootstrap.
(2) O evento que pega a chave e exibe dentro do formulário. Vou descrever esse evento a seguir:

As ações do Javascript podem ocorrer sobre vários objetos do formulário.
P. Como saber quais botões eu devo aplicar o evento ? Ou melhor, como selecionar apenas os botões do grid ?
R. Primeiro digo qual é o ID do grid: #registros.
Dou um espaço e vou selecionando os ítens "filhos" até chegar onde eu quero, que é o botão.

<table id="registros" class="table table-striped">
              <thead></thead>
              <tr><td> .... </td><td> .... </td><td> .... </td></tr>
              <tbody>
           <tr><td> .... </td><td> .... </td><td><button> AQUI </button></td></tr>
           <tr><td> .... </td><td> .... </td><td><button> AQUI </button></td></tr>
           <tr><td> .... </td><td> .... </td><td><button> AQUI </button></td></tr>
           <tr><td> .... </td><td> .... </td><td><button> AQUI </button></td></tr>
           </tbody>
              <!-- Dados -->
      </table>


Compare com : $("#registros tbody tr td button").click(function() ...

// Crio eventos para cada botão criado (não importa quantos)    
       $("#registros tbody tr td button").click(function() {
         $( "#content" ).html( $(this).closest("tr").attr('___pk') );
       });


*****************

E eu nem queria escrever muito sobre isso... Queria escrever sobre modHarbour.

Estamos nos distanciando do Harbour. Mas isso acontece também se você for programar em PHP, PYTHON, RUBY ou qualquer outra linguagem.
O HTML/CSS/Javascript é a base das interfaces web/mobile. Isso é unanimidade. Até a Microsoft entendeu isso. O Steve Jobs, da Apple, como era
visionário foi um dos primeiros a entender isso, acho que foi em 2008. Foi por isso que o Jobs boicotou a tecnologia Flash (alguém ainda se lembra do Flash ?) e preferiu adotar o HTML5 que ainda estava nascendo.
Na época ele preferiu adotar padrões abertos de interface, por dois principais motivos: economia de recursos (é caro desenvolver a própria interface)
e segurança (muitos olhos sobre o mesmo código garantem a segurança do mesmo).

Voltando ao assunto e finalizando.
O que eu quero dizer é : "mesmo que alguém crie um software que esconda tudo isso de você, a lógica será a mesma."

Os fontes :
ex14 - form modal.zip
(829 KiB) Baixado 7 vezes


Eu fiz uma pequena modificação. Deixei de exibir a chave primária e coloquei o campo "job" no lugar. Só deixei de exibir a chave, mas ela continua armazenada na tag ___pk de cada linha do grid. Senão eu não vou conseguir carregar o form de alteração, que será a próxima postagem.
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 vezes

Mini tutorial mod_harbour

Mensagempor Vlademiro » 21 Ago 2020 00:13

Outro pequeno passo.

O objetivo é preencher o formulário com os dados do banco. Só preencher. Ainda não tem rotina de alteração.

Caminho feliz :

(1) Usuário clica no botão ALTERAR (no grid)
(2) Sistema envia a variável "___pk" com o valor da chave primária para o form.prg (NOVO SCRIPT). Sistema abre o formulário vazio.
(3) form.prg (novo arquivo) recebe a variável "___pk", consulta no banco e retorna um json com um hash, por exemplo : { "ename":"SCOTT" , "job":"ANALYST" }
(4) navegador recebe esse hash e preenche os campos do formulário. Importante: o nome do campo do formulário deve ser o mesmo que o nome do campo na tabela.

Comentários.

(1) A parte 1 já foi vista nos exemplos anteriores.
(2) A parte 2 já foi vista nos exemplos anteriores.
(3) O código ADO que faz o preenchimento do Hash que conterá o retorno está descrito a seguir. Se quiser informações adicionais sobre o manuseio do ADO,
veja o código do adordd.prg de Antonio Linares, no código fonte do Harbour (pasta extras)

Aqui eu preencho o hash de retorno com os campos.
Atenção porque as referências (índices) usadas pelo ADO começam em zero (por isso coloquei x-1 no laço abaixo).
FOR x := 1 TO oRs:Fields:Count // Equivale ao FCount() do Clipper
    hReg[ oRs:Fields( x - 1 ):name ] := oRs:Fields( x - 1 ):value
NEXT   


Tive um "trabalhinho" para achar a propriedade que retorna o total de campos de um registro. Fui encontrar
nos fontes adordd.prg , que é a melhor fonte de consulta para quem usa o ADO.

(4) A recepção dos dados é feito no $.ajax do formulário. O laço for ... in recebe o hash json e faz o preenchimento dos campos.
for ( let elem in data )
   $( "#formModal" ).find( "#" + elem ).val( data[elem] );


Veja o contexto :
       $("#registros tbody tr td button").click(function() { <---------- Quando o usuário clicar no botão
          
           $.ajax({
                 type: 'POST', <----- Envio através do método POST
                 url: 'form.prg', <------- Para a URL
                 data: {
                      pk: $(this).closest("tr").attr('___pk') <------ Os seguintes dados
                  },
                 async: false,       
                 dataType: 'json',
                 success: function( data ){ <---- A função de usuário que pega o retorno de form.prg
                        for ( let elem in data ) <--- Percorro os elementos
                           $( "#formModal" ).find( "#" + elem ).val( data[elem] ); <-- Preencho

                 } 
           }); // ajax
       });



2020-08-17_131121.png


Lembrando:
1. Esse laço (for) é dentro da função que recebe o JSON (enviado pelo harbour).
2. A variável data contém o que o harbour enviou.
3. Como existe uma correspondência entre o nome do campo no banco de dados e o nome do campo no formulário, é só atribuir.
4. $( "#formModal" ).find( "#" + elem ).val( data[elem] );

Mais uma vez não se preocupe em entender tudo.
Anexos
ex15 - trazendo dados para o form.zip
(843.09 KiB) Baixado 5 vezes
Avatar de usuário

Vlademiro
Usuário Nível 4

Usuário Nível 4
 
Mensagens: 674
Data de registro: 11 Jul 2005 02:46
Curtiu: 14 vezes
Mens.Curtidas: 50 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 2 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