Showing posts with label Delphi Programming. Show all posts
Showing posts with label Delphi Programming. Show all posts

Friday, January 28, 2011

jQuery UI Dialog and tabindex attributes

Visual controls, like inputs, selects, etc., inside a jQuery UI modal dialog cannot have a tabindex attribute. If you do so, you won't be able to navigate using the TAB key. Simple isn't it?

Want to see for yourself?

1) First, go to the jQuery UI Dialog demo page, using Firefox + Firebug: http://jqueryui.com/demos/dialog/#modal-form

2) Click the button "Create new user"

3) Use Firebug to inspect the 3 inputs with id's: "name", "email" and "password". There is no tabindex attribute. Now, use Firebug and add the tabindex attribute with any value to these inputs. Any number like 1, 2, 3 should be ok.

4) Voilá! Once you are done you cannot navigate through the controls using TAB key anymore.

Workaround? Never put tabindex attributes in your HTML elements inside a jQuery UI modal dialog!

Wednesday, October 15, 2008

Cache de DataSets em aplicações multi-camadas

Há algum tempo utilizo mecanismos diversos de cache para melhorar a performance das aplicações que desenvolvo, principalmente com relação a acesso a bancos de dados. De uma forma mais aplicada venho desenvolvendo um mecanismo genérico de cache, para ser utilizado em aplicações multi-camadas. Problemas simples como o do post anterior (campos de lookup) podem ser muito beneficiados com mecanismo deste tipo.

É interessante ver que a maioria das aplicações mais "avançadas" ou elaboradas possuem algum mecanismo de cache mas no "mundo real" das aplicações comerciais que possuem acesso pesado a bancos de dados, o uso de um mecanismo de cache, por mais precário que seja, é raro ou inexistente.
O Delphi tem, out of the box, tudo que é necessário para fazer um cache nem tão rudimentar assim. Na verdade, depois de pronto, acho que poderia bater em muita coisa comercial que tem por aí... O ClientDataSet é um candidato perfeito para ser um DataSet genérico utilizado em um cache: é um DataSet em memória, pode ser gravado em disco em formato nativo (binário) e está presente na maioria das aplicações multi-camadas escritas em Delphi.

De forma geral e bem simplificada, o cache deveria funcionar da seguinte forma:
- Um DataSet é requerido à aplicação servidora (middle-tier) pela aplicação cliente;
- O mecanismo de cache (iremos chamá-lo de "gerenciador do cache") verifica se este DataSet já está no cache. Caso não esteja, a consulta normal ao banco de dados é efetuada. Caso o DataSet exista no cache, o gerenciador do cache deverá determinar se o cache está atualizado. Se estiver, o DataSet no cache é retornado à aplicação cliente sem necessidade de acesso ao banco de dados. Se não estiver, a consulta normal ao banco de dados é efetuada;
- Caso a consulta normal ao banco seja efetuada (o DataSet não está no cache ou o mesmo se encontra desatualizado) o gerenciador do cache se encarrega de incluir/atualizar o cache com este DataSet recém obtido do banco de dados;
- O gerenciador do cache deve prever acessos simultâneos de diferentes threads;
- O gerenciador do cache deve persistir e recuperar o cache em um repositório local (disco).

Simples não? Bem... não é tão simples mas também não é ciência de foguetes. Veremos que existem vários requisitos que deverão ser satisfeitos para que o mecanismo de cache seja viável. Entre eles, podemos citar:

1) O gerenciador do cache deverá ser capaz de identificar unicamente um DataSet;
2) O gerenciador do cache deverá ser capaz de identificar de forma inequívoca se o cache reflete a situação atual do banco de dados, ou seja, se o cache está atualizado no que tange àquele DataSet específico.

O requisito número 1 exige que o banco de dados se adapte à aplicação, ou seja, ao uso do cache. Os sistemas que desenvolvo já possuíam uma estrutura adequada, mesmo antes de um mecanismo de cache ser pensado. Então neste caso foi fácil.

Irei detalhar como satisfazer os requisitos 1 e 2 no próximo post.

Sunday, September 21, 2008

Campos Lookup - Uma abordagem racional

Ando lendo em sites e publicações especializadas em Delphi muita coisa sobre campos de lookup e sua utilização em sistemas, como por exemplo as famosas LookupComboBoxes. Geralmente fala-se mal, recomenda-se não utilizar campos lookup. Ou então que sua utilização é muito restrita, e só serve para tabelas contendo poucos registros (exatamente quantos, ninguém fala, certo?).. Coisas que eu ouvia há 10 ou 15 anos, quando quase todo mundo desenvolvia usando os velhos e bons conceitos Cliente/Servidor.

De lá prá cá muita coisa mudou. A banda de internet que a alguns têm em casa é mais rápida que algumas redes que usavam cabos coaxiais em 1995.
Mesmo assim, alguns teimam em usar o mesmo conceito antigo que "não se pode trafegar pela rede meia dúzia de registros que possivelmente não serão usados"... AFFFFF!!!!
Então vejamos... O que é mesmo AJAX? Quanta informação desnecessária é trafegada para dar a uma aplicação web AJAX o seu comportamento?
De qualquer forma, nada melhor que fatos e dados para basear o meu ponto de vista.

Suponha uma tabela "cliente", com a seguinte estrutura:

codigo_cliente - Integer
nome_cliente - varchar(50)
codigo_profissao - Integer

e uma tabela "profissao" com a seguinte estrutura:

codigo_profissao - Integer
descricao_profissao - varchar(30)

Em 99% dos casos eu ouço que não se deveria fazer lookup na tabela de profissões, e sim um JOIN.
Imagine uma consulta típica para um relatório, onde irei listar os clientes e suas profissões. O SQL típico seria:

SELECT c.codigo_cliente, c.nome_cliente, p.descricao_profissao
FROM cliente c
INNER JOIN profissao p
ON c.codigo_profissao = p.codigo_profissao
ORDER BY c.codigo_cliente ASC

Com alguma simplificação podemos assumir um tamanho de registro de dados igual a 84 bytes. Se eu tivesse 2000 clientes, teríamos um pacote de dados de aproximadamente 165 kbytes.

Agora, vejamos por outro lado. Imagine que eu tivesse 2 consultas separadas:
SELECT codigo_cliente, nome_cliente, codigo_profissao
FROM cliente
ORDER BY codigo_cliente ASC

SELECT codigo_profissao, descricao_profissao
FROM profissao

Vamos supor ainda que minha tabela de profissão contivesse 500 registros. O total de dados trafegados seria 58*2000 (clientes) + 34*500 (profissões) = 130 kbytes.

Oras! Eu fiz duas consultas, trouxe toda a tabela de profissões para o lado cliente e ainda tive um tráfego menor???? Hum... Agora, imagine que este relatório é chamado por várias vezes durante a execução do programa?

Quantas vezes por dia a tabela de profissões muda? Talvez sejam adicionadas novas profissões, de vez em quando, mas em sistemas do mundo real isto ocorre muito no início do ciclo de vida do sistema e tende a diminuir - e muito - com o tempo. Não poderíamos carregar a tabela de profissões, digamos na inicialização do sistema, e usá-la como Lookup durante o ciclo de vida da aplicação?

Um mecanismo mais inteligente poderia checar as alterações da tabela de profissões antes de sua utilização (como lookup no relatório, por exemplo) e fazer um refresh sempre que necessário. O ganho de performance tanto de banco de dados quanto da aplicação é significativo.

Alguns conceitos de C/S estão certamente ultrapassados. O pessoal que os usa sem parar para pensar (e principalmente MEDIR) o que acontece de fato em sistemas do mundo real deveria revê-los.

Saturday, August 9, 2008

Novo!! Fórum de discussão da CodeGear

Está funcionando desde 7 de agosto o fórum de discussão da Embarcadero (e CodeGear) no endereço https://forums.codegear.com

Ainda é beta, mas parece bom.

Pelo visto, eu fui o primeiro a registrar e postar do Brasil! :)

O lançamento do fórum foi adiantado pelo crash do servidor de newsgroups, que pelo visto foi feio - há dois dias o newsgroup da CodeGear está offline.

Thursday, June 26, 2008

ExtPascal - Pascal para Ext JS

Legal! Wanderlan Anjos publicou uma versão inicial de seu framework ExtPascal, um wrapper para as bibliotecas Ext JS.
Está em seus estágios iniciais mas vale uma olhada. Baixei os fontes e irei criar um projeto piloto para verificar a possibilidade de utilização em algum projeto! Parabéns Wanderlan!