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

Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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

Código: Selecionar todos

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

Código: Selecionar todos

     $("#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)

Código: Selecionar todos

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.

Código: Selecionar todos

 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 634 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 do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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.

Código: Selecionar todos

 <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.

Código: Selecionar todos

   $("#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.

Código: Selecionar todos

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 :

Código: Selecionar todos

 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 do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Mini tutorial mod_harbour

Mensagem por Itamar M. Lins Jr. »

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.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

Ainda não cheguei lá.

Mas com certeza é com Cookies.

Código: Selecionar todos

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 do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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 é :

Código: Selecionar todos

{
 "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 :

Código: Selecionar todos

{"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:

Código: Selecionar todos

    /* 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

Código: Selecionar todos

	/* 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)

Código: Selecionar todos

	/* 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 610 vezes
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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 do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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.

Código: Selecionar todos

  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.

Código: Selecionar todos

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 493 vezes
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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.

Código: Selecionar todos

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.

Código: Selecionar todos

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 :

Código: Selecionar todos


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

Código: Selecionar todos

<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

Código: Selecionar todos

	<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) Exibido 7716 vezes
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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.

Código: Selecionar todos

{ "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 :

Código: Selecionar todos

<datalist id="dept"></datalist>
2.2. Agora vou criar um evento que irá ser ativado a cada tecla pressionada no campo de busca.

Código: Selecionar todos

	$("#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 525 vezes
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Mini tutorial mod_harbour

Mensagem por Itamar M. Lins Jr. »

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.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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 :

Código: Selecionar todos

  <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.

Código: Selecionar todos

local hPost := AP_PostPairs()

        ?? hb_ValToExp( hPost )

E vai imprimir :

Código: Selecionar todos

{"meuArray"=>{"1", "2", "3", "4"}}
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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 :

Código: Selecionar todos

{"meuArray"=>"4"}
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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)

Código: Selecionar todos

TEXT SQL TO cSQL
  
	SELECT deptno, dname
		FROM dept
	WHERE dname LIKE '%:word%'    
	ORDER BY dname ASC

ENDTEXT
Durante a leitura preencho o JSON :

Código: Selecionar todos

	/*
	0 = Código
	1 = Descricao
	*/
	hReg[ "valueId" ] := oRs:Fields( 0 ):value
	hReg[ "valueDesc" ] := oRs:Fields( 1 ):value
O retorno terá essa aparência :

Código: Selecionar todos

{ "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.

Código: Selecionar todos

<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

Código: Selecionar todos

<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.

Código: Selecionar todos

<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

Código: Selecionar todos

	$("#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) Exibido 7687 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/ ... 052102244/
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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.

Código: Selecionar todos

   <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.

Código: Selecionar todos

 <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.

Código: Selecionar todos

$( 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) Exibido 7667 vezes
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Mini tutorial mod_harbour

Mensagem por Vlademiro »

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 599 vezes
Responder