sábado, 7 de outubro de 2017

Bootstrap Navbar

Nosso artigo Menu Recursivo foi o que trouxe a maior repercussão no Genexando ao longo dos anos, pois foram mais de 12 mil visualizações desde sua concepção em 2010, e ainda é procurado. Casos de sucesso, mais que comemorados, devem nos servir para apontar o interesse da nossa pequena comunidade, sendo assim, vamos revisitar o conceito do menu, agora com mais recursos.

O fato é que desde 2010, algumas coisas mudaram no mundo Genexus. Olhando nosso artigo, já ancião, chegamos à conclusão que ele já não se encaixa mais na nova geração de aplicações. O User Control JsCookMenu com certeza foi um sucesso nas nossas aplicações, agradecimentos ao Daniel Coellar, mas hoje, com o advento da responsividade, Bootstrap e outros, ficou no passado.

A pergunta é, como montar um menu para nossa aplicação? Creio que a pergunta já indicaria um caminho, pois, atualmente as aplicações se suportam sobre um tipo distinto de menu: Navbar, e neste sentido, temos um pequeno problema, pois Genexus não oferece um User Control que faça isso nativamente.


Este controle, ao ser apresentado em uma MasterPage, por exemplo, permitiria acessar às interfaces do sistema, por meio de uma barra superiora, visualmente bem formatada e visivel.

Bootstrap

Um caminho para se chegar ao Navbar é utilizar as classes nativas do Bootstrap, que disponibiliza um componente muito simples para se construir a barra (mais informações aqui), baseado em tags e classes do framework. É um mecanismo muito simples, porém envolve HTML e CSS.

Nem todos controles Bootstrap poderemos construir com a estratégia simplificada que estaremos apresentando neste artigo, porém, Navbar cabe perfeitamente, porque o que deveremos criar são textos com links, não havendo a necessidade de retornar alguma coisa ao Genexus ou mesmo controlar eventos.

A implementação de User Controls sempre será a melhor opção, e já oferecemos nossa contribuição à comunidade, pois os argumentos em seu benefício seriam a reutilização em projetos com facilidade, a possibilidade de comunicação e retorno de eventos. E o lado negativo dos controles é, em meu ponto de vista, a rigidez visual ou operacional implementada no código, que é muito difícil de ajustar. O fato é que para criar um User Control, precisaremos estudar um pouco, umas 500 páginas, e depois que se aprende poderemos dizer que é simples, mas para um contato inicial soa bastante complexa a quantidade de definições e detalhes.

Temos, por outro lado a possibilidade de implementar código HTML diretamente na nossa interface, e para isso somente precisamos de um único controle Textblock, definido como HTML. Poderíamos buscar outras metodologias mais ‘bonitinhas’, mas vamos simplificar um pouco aqui.

Desta forma, vamos, neste exemplo implementar um Navbar com Bootstrap e Textblock, e para incluir uma cerejinha no topo, GAM Menu.

GAM Menu

Outro recurso que nasceu foi o GAM Menu, um mecanismo que permite criar o menu do sistema com as permissões do próprio GAM. Isto foi implementado no Genexus 15. Aqui alguns links que tratam do assunto, inclusive um que liga o GAM Menu com o JsCookMenu (muito interessante!):
No GAM Menu deveremos criar uma lista de itens, associando a cada um deles o link correspondente à uma permissão do GAM. Isto se faz no GAMHome - Applications - Menu - Add

A permissão é uma referência a uma interface na Kb. Por exemplo build_Execute é a permissão ao objeto build (WebPanel), e cada item do Menu deverá apontar para uma permissão.


Não vou gastar muita energia explicando tudo isso, porque GAM é um dos temas mais documentados de Genexus, basta acessar ao Wiki e terá muita documentação à sua disposição.

O que deveremos fazer, neste exemplo, é acessar os itens definidos no nosso menu GAM, e para isso necessitaremos de algumas coisas, a primeira é o GUID da nossa aplicação, e também o GUID do nosso menu. 

Ambos os valores são gerados quando criamos um novo Menu e quando aplicamos o GAM à nossa Kb. Poderemos obter o ApplicationId via programação, mas também, se você não tiver muita paciência para programar o GAM, pegar o valor diretamente nas Preferences da Kb e no GAMHome, e fixar no código.


Via programação, o &ApplicationGUID pode ser obtido pela sessão do usuário conectado na aplicação e o &MenuGUID, a partir da GAMApplication identificada. O problema é se houver mais que um menu associado à aplicação, então teremos que alterar o item(1), para se apontar para o menu correto.

GAMDefinitions:
Rules:
parm(&ApplicationGUID, &MenuGUID);

Source:
&GAMSession = GAMSession.Get(&GAMErrors)
for &GAMApplication in  &GAMRepository.GetApplications(&GAMApplicationFilter, &GAMErrors)
 if &GAMSession.ApplicationId = &GAMApplication.Id
  exit
 endif
endfor
&ApplicationGUID = &GAMApplication.GUID

&GAMApplicationMenus = &GAMApplication.GetMenus(&GAMApplicationMenuFilter, &GAMErrors)
if &GAMApplicationMenus.Count>0
  &MenuGUID = &GAMApplicationMenus.Item(1).GUID.ToString()
endif

A seguir as variáveis utilizadas no programa.


Programamos esta operação em uma procedure que chamamos de GAMDefinitions. E esta é chamada antes de se obter o menu, com outra operação simples, veja a seguir.

GAMDefinitions(&ApplicationGUID, &MenuGUID)
&GAMMenuOptionList = GAMRepository.GetApplicationMenu(&ApplicationGUID, &MenuGUID, &GAMErrors)

Onde &GAMMenuOptionList é a lista de itens do menu, (do tipo GAMMenuOptionList). Neste objeto temos uma opção Nodes com a definição de todos os itens do menu.

Criando o NavBar

A criação do NavBar é um pouco estranha, porque teremos que definir uma estrutura HTML e incluir nesta nossos itens de Menu. Programei uma procedure chamada Navbar, com o seguinte conteúdo

Navbar:
Rules:
parm(&Menu);

Source:
//navbar
&Menu  = '<div class="container-fluid">'
&Menu += ' <nav class="navbar navbar-default navbar-fixed-top " role="navigation">'

// collapse menu, 
&Menu += '  <div class="navbar-header navbar-left pull-left" style="">'
&Menu += '   <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">'
&Menu += '    <span class="icon-bar" ></span>'
&Menu += '    <span class="icon-bar" ></span>'
&Menu += '    <span class="icon-bar" ><span>'
&Menu += '   </button>'
&Menu += '   <a class="navbar-brand" href="#">logo</a>'
&Menu += '   <div class="navbar-brand">sistema</div>'
&Menu += '  </div>'

// barra do menu
&Menu += '  <div id="navbar" class="navbar-collapse collapse">'
do 'gammenu'
&Menu += '  </div>'

// encerramento
&Menu += ' </nav>'
&Menu += '</div>'

sub 'gammenu'
 GAMDefinitions(&ApplicationGUID, &MenuGUID)
 &GAMMenuOptionList = GAMRepository.GetApplicationMenu(&ApplicationGUID, &MenuGUID, &GAMErrors)
  NavbarItens(&GAMMenuOptionList, &Menu)
endsub

Em negrito destacamos a parte dinâmica do menu, em si, na qual estaremos agregando os itens obtidos do GAM Menu.

As variáveis da procedure são apresentas a seguir.

Os diversos itens do Navbar são alcançados por uma outra procedure chamada NavBarItens que deve ser recursiva. Observe que antes do endfor, colocamos a chamada recursiva para pegar os itens associados ao elemento corrente.

NavbarItens:
Rules:
parm(&GAMMenuOptionList, &Menu);

Source:
for &GAMMenuOption in &GAMMenuOptionList.Nodes
 &link = &GAMMenuOption.Link.ReplaceRegEx(' ','')
 &Menu += '   <ul class="nav navbar-nav">'
 &Menu += '   <li class="nav navbar-item"><a href="'+&link+'">'+&GAMMenuOption.Name+'</a></li>'
 &Menu += '   </ul>'
 MenuSubGAM(&GAMMenuOption, &Menu)
endfor

A procedure recebe a variável &Menu (Longvarchar) e a passa para a MenuSubGAM, e cada chamada irá complementar o Navbar com o item no menu. Abaixo as variáveis programadas.


MasterPage

Para utilizar essa maluquice toda, precisaremos apenas de um Textblock, marcado como HTML, na MasterPage da nossa Kb.

Event Start
   textblock1.caption = Navbar()
Endevent  

O resultado será a apresentação do primeiro Menu definido no GAMHome com todas as suas opções vinculadas no NavBar.

Restrições

  • Neste primeiro exemplo não estamos tratando os submenus, ou seja, Dropdown que venham a ser vinculados a certo item de menu. E não fizemos isto porque não fica muito claro no GAM a identificação dos níveis de menus. Aguardemos as cenas dos próximos capítulos.
  • Este exemplo funciona naturalmente no Genexus 15, se for aplicado ao Genexus Ev3 a operação somente funcionará em modo Smooth.

XPZ

Antes de importar o xpz é necessário aplicar o GAM na sua Kb e em seguida definir a partir do GAMHome um menu para a sua aplicação. Se você não aplicar o GAM e também não definir o menu, a importação do mesmo causará erro.

O xpz com os exemplos apresentados encontra-se aqui.

Após a importação é necessário que altere a master page do seu sistema para o objeto NavbarMasterPage.