pelo autor convidado Michael Höhne, 4D developer (Munique, Alemanha)
Há uma característica no 4D v18 R5 que pode ter sido negligenciada, ou pelo menos não ter recebido muita atenção até agora: Formar macros. Para ser honesto, também não tinha passado muito tempo nelas, até há pouco tempo. Neste post do blog, vou mostrar-vos uma macro que poupa muito tempo ao aplicar convenções de nomes a colunas de caixas de listagem, cabeçalhos de colunas e rodapés. Pode facilmente alterá-la de acordo com as suas necessidades. Um repo dedicado está também disponível no Github.
contexto primeiro …
Utilizamos macros no nosso trabalho diário para facilitar o desenvolvimento e é uma característica muito fixe. Como muitas outras empresas, também temos directrizes de codificação. São essenciais quando se trabalha em equipas maiores para compreender mais facilmente o código umas das outras. A linguagem 4D não mudou durante muito tempo e as nossas directrizes também não. Depois vieram ORDA, classes, e novas sintaxes para declarações de variáveis e métodos! Era tempo de incorporar estes novos elementos linguísticos e fazer uma revisão geral das nossas especificações internas. Parte dessa revisão era formar nomes de objectos (embora totalmente sem relação com o novo material). Falámos de caixas de verificação, botões de rádio, campos, e finalmente listamos caixas. Não vou escrever sobre os nossos estilos de nomenclatura. O importante é que uma caixa de listagem não tem um único nome de objecto. Tem nomes para a lista em si, cada coluna, e cada cabeçalho e rodapé de página da coluna. Uma simples caixa de listagem com 5 colunas tem 16 nomes de objectos. Se quiser aplicar convenções de nomes a uma caixa de listagem, pode encontrar-se num processo enfadonho. Não seria bom automatizá-lo?
Nomes de objectos para uma caixa de listagem contendo empresas poderiam ser:
- listCompanhias para a lista em si,
- listCompaniesCol1, listCompaniesCol2, …, para os nomes das colunas,
- listCompaniesCol1Header, listCompaniesCol2Header, …, para os cabeçalhos das colunas,
- listCompanhiasCol1Footer, listCompanhiasCol2Footer, …, para os rodapés das colunas.
Esta é apenas uma forma possível de o fazer. Se perguntar aos criadores sobre as suas preferências de nomenclatura, muito provavelmente terá muitas opiniões diferentes. Contudo, pode haver um padrão semelhante aos nomes acima: todos os nomes de colunas, cabeçalhos e rodapés são baseados no nome da caixa de listagem. Estes nomes podem ser atribuídos por uma macro de formulário!
Início Rápido
As macros do formulário estão disponíveis através do menu contextual do desenhador do formulário:
Se nunca utilizou macros de formulário antes, então é provável que o seu menu de contexto não inclua de todo um menu de Macros:
Neste caso, a primeira coisa a fazer é criar um ficheiro formMacros.json e colocá-lo na pasta Sources do seu projecto:
O ficheiro formMacros.json define os nomes dos itens do menu e as classes que implementam o código. O ficheiro que estou a utilizar neste exemplo é muito simples:
{ "macros": { "Rename Listbox columns": { "class": "RenameListboxColumns" } } }
Diz ao 4D para adicionar um item de menu chamado “Renomear colunas da caixa de listagem” ao menu Macros (mostrado no menu de contexto do designer do formulário). Ao clicar no item de menu, chamará o método onInvoke de uma classe chamada “RenameListboxColumns” (mais sobre isso em breve). O ficheiro formMacros.json é lido uma vez quando o projecto é carregado. As alterações ao conteúdo não são aplicadas ao UI até que o projecto seja recarregado. Se criou o ficheiro formMacros.json agora mesmo, então feche o seu projecto e abra-o novamente para ver o menu Macros no desenhador do formulário. Informação detalhada sobre as macros do Form está disponível aqui.
As macros de formulário são classes
A implementação de uma macro Forma está dentro de uma classe. A propriedade “classe” na definição da macro (“classe”: “RenameListboxColumns”) define o seu nome. Para que a nossa macro faça qualquer coisa, precisamos de criar essa classe:
Para ver se funciona, acrescente-lhe o seguinte código:
// Macro invoked in the form designer.
Function onInvoke($editor: Object)->$return: Object
ALERT ("Hello world!") // Return the modified page object
$return :=New object("currentPage"; $editor.editor)
Quando seleccionar as colunas “Renomear caixa de listagem” no menu Macros, deverá receber o alerta:
Esta é uma verificação básica para verificar se tudo está configurado correctamente. Para mostrar uma possível armadilha, altere o código para:
// Macro invoked in the form designer.
Function onInvoke($editor: Object)->$return: Object
This .showHelloWorldMessage() // Return the modified page object
$return: =New object("currentPage"; $editor.editor)
Function showHelloWorldMessage()
ALERT ("Olá mundo!")
Seleccione novamente o menu “Renomear colunas da Listbox”:
4D carrega a definição de classe apenas uma vez. É possível alterar a implementação dos métodos de classe, mas novos métodos ou alterações aos parâmetros do método não estão disponíveis até que se recarregue o projecto. Isto é um pouco inconveniente, mas habitua-se a isso. Depois de recarregar o projecto, o código funcionará muito bem.
Adicionando funcionalidade
Agora que sabe como criar uma macro Form, vamos adicionar alguma funcionalidade! Exibir uma mensagem de Olá Mundo é agradável, mas faria sentido que uma macro chamada “Renomear colunas da caixa de listagem” fizesse mais. Renomear colunas, por exemplo.
O método onInvoke passa um objecto chamado $editor. Este objecto contém informação sobre o formulário em que está actualmente a trabalhar. currentSelection ($editor.editor.currentSelection) contém os nomesdos objectos de todos os objectos seleccionados. Se ainda não seleccionou nenhum, este estará vazio.
currentPage ($editor.editor.currentPage ) contém todos os objectos que se encontram na página do formulário actual. form ($editor.editor.form) contém o formulário completo. Estes objectos são simplesmente partes da declaração JSON dos formulários. Se quiser ter uma visão geral das estruturas dos objectos, basta abrir o ficheiro do formulário.4DForm em que está a trabalhar, num editor de texto.
Uma vez que queremos renomear os nomes dos objectos das colunas da caixa de listagem, cabeçalhos das colunas e rodapés, a lógica principal da aplicação será:
- Percorra os objectos seleccionados. Para cada caixa de listagem encontrada, faça o seguinte:
- Faça um loop através das colunas da caixa de listagem e calcule os nomes dos objectos para cada coluna, cabeçalho de coluna e rodapé de página com base num padrão específico.
- Se um dos nomes de objectos calculados já for utilizado no formulário, então abortar o processo de renomeação e exibir uma mensagem de erro para o utilizador.
- Caso contrário, renomear o objecto.
Comecemos com o primeiro tópico:
- Percorrer os objectos seleccionados. Para cada caixa de listagem encontrada, faça os passos descritos anteriormente:
// Macro invoked in the form designer.
Function onInvokeeditor($editor: Object)->$return: Object
var $objectName : Text
editor $formObject : Object
This .editor:=$editor.editor
If (This.editor.currentSelection.length>0)
For each ($objectName; Isto. . var )
$formObject :=This.editor.currentPage.objects[$objectName]
If (This.isListbox($formObject))
This .renameListboxColumns
// Return the modified page object ($objectName; $formObject)
End if
End for each End if
$return :=New object("currentPage"; This.editor.currentPage)
Este código corresponde à frase (faça um laço através dos objectos seleccionados, verifique as caixas de listagem, e renomeie as suas colunas). Mas como é que identificamos uma caixa de listagem? Vamos colocar um ponto de quebra no código e investigar:
Cada objecto tem uma propriedade type. Para uma caixa de listagem, está definida como “caixa de listagem”. Isto é o mesmo que se vê ao abrir o ficheiro form.4dform num editor de texto:
// Check if the form object is a listbox
Function isListbox($formObject: Object)->$isListbox: Boolean
$isListbox :=($formObject.type="caixa de listagem")
Muito fácil! É claro que este “one-liner” poderia ser incluído directamente em onInvoke em vez de declarar um novo método, mas após cerca de 30 anos de desenvolvimento orientado para objectos, estou habituado a criar pequenos métodos. Ajuda a escrever códigos fáceis de compreender e fáceis de manter.
Agora que sabemos como identificar uma caixa de listagem, vamos dar o passo seguinte:
- Percorrer as colunas da caixa de listagem e calcular os nomes dos objectos para cada coluna, cabeçalho de coluna, e rodapé de página com base num padrão específico.
// Renames all columns, column headers and footers based on
// the listbox object name.
Function renameListboxColumns( : ; : ) : : : :=1 ( ; . ) $lbxName Text $listbox Object
var $col Object
var $index Integer
var $newObjectName Text
$index
For each$col $listboxcolumns
This . ( ; +"Col "+ ( )) setObjectName$col $lbxNameString$index
This . ( . ; . +"Footer") setObjectName$colfooter $colname
This . ( . ; . +"Cabeçalho") setObjectName$colheader $colname
$index := +1$index
End for each
A renomeação de um objecto não deve levar à duplicação de nomes de objectos.
- Se um dos nomes de objectos calculados já for utilizado no formulário, então abortar o processo de renomeação e exibir uma mensagem de erro para o utilizador.
- Caso contrário, renomear o objecto.
// Changes the object name of $formObject to the $newObjectName.
// If the new object name is different than the current name
// and form object with that name already exists on any page
// of the form, the processing is aborted and a message is shown in the designer.
Function setObjectName( : ; : ) ( # . ) ( . ( . . ; )) ("Nome do objecto " +" já utilizado. Renomeação cancelada") $formObject Object $newObjectName Text
If$newObjectName$formObjectname
IfThisisObjectNameUsedInFormThiseditorform $newObjectName
ALERT$newObjectName
ABORT // abort further processing
Else
$formObject. :=name$newObjectName
End if
End if
A verificação de um nome de objecto existente é feita com mais alguns métodos: isObjectNameUsedInForm, isObjectNameUsedInPage, e isObjectNameUsedInListbox. Estão incluídos na amostra final disponível no Github. Contém também um formulário que pode ser utilizado para testar a macro:
Utilização de macros de formulário no seu trabalho diário
Se a implementação de uma macro Forma é específica a um único projecto, então faz todo o sentido adicioná-la directamente a esse projecto. Mas macros como esta podem ser úteis em qualquer projecto que contenha formulários. Pode, claro, copiar a classe para cada projecto e adicionar a definição da macro a cada ficheiro FormMacros.json, mas é mais fácil criar um componente em vez disso.
Um componente de macro de formulário não é mais do que um projecto 4D com um ficheiro formMacros.json e as classes definidas no mesmo. A única diferença é que constrói um componente e coloca-o na pasta de componentes apropriados do seu projecto, ou coloca-o na pasta de componentes da aplicação 4D para o ter disponível em todos os projectos, por defeito.
Se apenas quiser utilizar a macro e não se preocupar com a implementação: a amostra no GitHub inclui o componente compilado na pasta Build.