terça-feira, 19 de outubro de 2010

Criando um Menu Recursivo (revisado)

Às vezes o mais complexo é resolvido com coisas simples, na verdade me parece que essa é uma regra da natureza, pois quanto mais complexa é a tentativa, mais distante se está da solução. Claro que quando falamos de programação de computadores, essa regra é quebrada de vez em quando, principalmente quando se depara com algoritmos com certo grau de dificuldade.

E na classe dos algoritmos mais estranhos, e que faz com que os programadores pensem duas vezes, encontram-se os recursivos. Ou seja, aqueles que por alguma razão precisam chamar a si próprios.

Acontece que uma das aplicações mais interessantes desses algoritmos, se encontram exatamente os menus de opções, e planejar um bom menu para o usuário é uma das tarefas importantes do desenvolvimento, para dar acessibilidade, organização e clareza nas operações do sistema.

A boa notícia é que Genexus é 10! e alguns programadores muito bem intencionados nos deram todos os recursos para programar um menu recursivo, sem muito esforço, portanto, cabe aqui apenas o direcionamento para os recursos corretos.
Entao, maos na massa e um pouco de paciencia para entender os elementos do menu.
 

1. JSCookMenu:

Esse é um controle muito pratico, que possibilita que se construam menus na interface web de forma muito interessante com controle sobre as classes CSS, ou seja, estilos, imagens, ...

Para carregar o JSCookMenu é necessário utilizar uma SDT, que já é recursiva por natureza, pois os Itens (Item) são na verdade ramos da própria SDT, como pode ser visto abaixo (veja o Type).

O menu possui uma variável do tipo coleção do SDT JsCookMenuItem que ao ser carregado apresenta as opções no controle, se olhar nas Variables verá que foi incluida uma variavel chamada &MenuDataCollection para esta finalidade. Portanto, para se carregar um item no menu teriamos que programar algo como:

 

&JSCookMenuItem = new()
&JSCookMenuItem.Title = 'item'

&JSCookMenuItem.UTL = 'http://www.gotoitem.com/itemfolder/item'
&MenuDataCollection.Add(&JSCookMenuItem)

 
Desta forma para cada item a ser incluido no menu teriamos que gerar um conjunto igual a este de codigo.Se não entendeu este código melhor voce estudar um pouquinho a respeito de Structured Data Type e também Collections.

 


2. Data Provider:

Para programarmos o menu recursivo usando a programação apresentada anteriormente seria bastante complexo, com for each, procedure recursiva, enfim, vamos aproveitar o exemplo para falar de mais um recurso interessante que temos a nosso favor.
 
A Artech comemorou muito o lançamento deste recurso no Genexus, é não é pra menos, com poucas linhas de ‘código’, se carrega uma SDT complexa como a apresentada, bastando declarar o conteúdo que desejamos em cada elemento da estrutura.

Para ser mais claro, o DP abaixo dá conta do recado e substitui a programação da procedure mais complexa, apenas usando o recurso de declarar o conteudo que queremos.



Os elementos incluidos nesta programação são atributos que estão em uma tabela Menu, e prontos para irem para nosso controle na interface (MenuTitulo, MenuTarget, FatherMenuId, ...)


Um detalhe que nao explicamos da primeira vez aqui foi a regra PARM necessária para este Data Provider funcionar, que deve ser:


Parm(&FatherMenuId, &FatherMenuItemId);


Falando em recursividade, se observar bem verá que existe um elemento na estrutura (childs) que chama o próprio Data Provider (JsCookMenuProvider1), ou seja, o SDT foi criado para funcionar de forma recursiva.


Quando este artigo foi escrito pela primeira vez, a idéia era uma tabela mais complexa, agora analisando com calma, a tabela Menu poderia ter sido criada mais simples apenas com os atributos:
  • MenuId
  • MenuTitulo
  • MenuUrl
  • MenuPaiId (subtipo apontando para MenuId)
 

3. Web Panel:

Aqui a coisa é simples, somente inclua um user control JSCookMenu na interface, e em seguida carregue a variável &MenuDataCollection com o nosso DP recursivo. Passe o parâmetro 1,1 na ‘primeira chamada’, e no evento Start do Webpanel chame o MenuSample.





4. O Resultado:

O resultado alcançado com esse Data Provider é bem legal, com as opções principais, sub menus e por ai vai, nao existe limite para organizar os niveis no Menu, pois, lembre que é recursivo.

Temos ai um recurso de árvore!! Fala sério, ficou legal, hein!!



5. A Árvore:

Observe que a parte programável acabou! E não gastamos muito para chegar aqui (Uma transação Menu, Um DataProvider carregando uma coleção do tipo SDT JSCookMenuItem), e você deve estar se perguntando cadê a procedure, onde esta o codigo fonte necessário para se carregar a coisa?. A resposta é que o Data Provider faz tudo, é uma 'procedure declarativa', e pode funcionar ainda como caracteristica recursiva.


Se não temos muito o que fazer com o programa então vamos explicar à parte mais difícil que consiste em alimentar o monstro, ou seja, como armazenar e carregar informação recursiva que alimente o DP e o JSCookMenu.



Para isso vou recorrer a um desenho de árvore mesmo, que talvez simplifique um pouco a visão para você. Veja que pendurado no nó 2 temos os itens (5, 6 e 7), e no nó 6 os itens (8 e 9). Outro exemplo, pendurado no nó 1, temos os nós (2, 3 e 4), e assim por diante. Entenda que o Menu pode ser organizado desta forma, bastando voce escolher onde vai pendurar as coisas.

Para os ‘puristas’ e mais exigentes, pode-se dizer que esta árvore não é binária, portanto, para cada nó, temos vários que podem estar conectados.



Em termos de dados podemos dizer que precisamos apenas de um numero para definir o nó e outro para indicar a qual nó este pertence. Ou seja, nó=7, pertence ao nó=2. E por ai vai.
]

Portanto, simplificando a coisa, precisaremos de uma tabela com os seguintes dados, para produzir o exemplo anterior:




E o que a tabela tem de especial? Apenas um campo recursivo chamado FatherMenuId e FatherMenuItemId que aponta para MenuId e MenuItemId, respectivamente. Observe que utilizei dois números para indicar um nó. Entenda o primeiro como grupo e o segundo como item do grupo, tipo uma idéia para organizar menus por grupos de usuários.


Aqui também faltou na explicação que para apontar FatherMenuId e FatherMenuItemId para MenuId e MenuItemId é necessário um objeto chamado Subtipo no Genexus.



6. Transação:

Claro que estamos no Genexus, e para que tudo se complete é necessária uma transação que grave os dados. Para isso temos uma estrutura simples:




Sendo que FatherMenuId e FatherMenuItemId são subtipos que apontam para MenuId e MenuItemId.



Agradecimentos e Conclusão

Portanto, para se criar um menu siga os passos:
  1. Crie uma transação Menu
  2. Crie um WebPanel e inclua nele o controle JSCookMenu
  3. Crie um DataProvider para carregar o SDT JsCookMenuItem
  4. Chame o DataProvider no WebPanel
Se voce usar sua imaginação poderá entender que este mesmo conceito pode ser usado para tratar de coisas diferentes, como por exemplo uma árvove hierárquica de familia, controle genetico de animais, ...


A conclusão aqui é simples, Genexus proporciona uma programação em árvore, complexa, com poucas linhas de código no Data Provider, então, reclamar do que? Genexus é 10!



Já o agradecimento é complexo, porque tantas pessoas participaram da criação do JSCookMenu, do Genexus, Transação, Subtipo, Data Provider, treinamento da Artech, ... que fica impossível identificar todos, e para não deixar ninguém de fora desta, então vamos apenas dizer:
OBRIGADAO!!!



Se quiser saber um pouco mais sobre recursividade em Data Provider, pode dar uma olhada no link: http://wiki.gxtechnical.com/commwiki/servlet/hwiki?Recursive+Data+Providers,