por el autor invitado Michael Höhne, desarrollador de 4D (Munich, Alemania)
Hay una característica en 4D v18 R5 que puede haber sido pasada por alto, o al menos no ha recibido mucha atención hasta ahora: Las macros de formulario. Para ser sincero, yo tampoco les había dedicado mucho tiempo, hasta hace poco. En esta entrada del blog, te mostraré una macro que ahorra mucho tiempo a la hora de aplicar las convenciones de nomenclatura a las columnas de los cuadros de lista, los encabezados de las columnas y los pies de página. Puedes cambiarla fácilmente para adaptarla a tus necesidades. Un repo dedicado también está disponible en Github.
contexto primero …
Usamos macros en nuestro trabajo diario para facilitar el desarrollo y es una característica realmente genial. Como muchas otras empresas, también tenemos directrices de codificación. Son esenciales cuando se trabaja en equipos más grandes para entender el código de los demás más fácilmente. El lenguaje 4D no cambió durante mucho tiempo y tampoco nuestras directrices. Luego llegaron ORDA, las clases y las nuevas sintaxis para las declaraciones de variables y métodos. Era el momento de incorporar estos nuevos elementos del lenguaje y realizar una revisión general de nuestras especificaciones internas. Una parte de esa revisión fue la de los nombres de los objetos de los formularios (aunque no tiene nada que ver con lo nuevo). Hablamos de las casillas de verificación, los botones de radio, los campos y, por último, los cuadros de lista. No voy a escribir sobre nuestros estilos de nomenclatura. Lo importante es que un cuadro de lista no tiene un solo nombre de objeto. Tiene nombres para la propia lista, para cada columna, y para cada encabezado y pie de columna. Un cuadro de lista simple con 5 columnas tiene 16 nombres de objetos. Si quiere aplicar convenciones de nomenclatura a un cuadro de lista, puede encontrarse con un proceso tedioso. ¿No sería bueno automatizarlo?
Los nombres de objetos para un cuadro de lista que contenga empresas podrían ser
- listCompanies para la propia lista,
- listCompaniesCol1, listCompaniesCol2, …, para los nombres de las columnas,
- listCompaniesCol1Header, listCompaniesCol2Header, …, para las cabeceras de las columnas,
- listCompaniesCol1Footer, listCompaniesCol2Footer, …, para los pies de columna.
Esta es sólo una forma posible de hacerlo. Si preguntas a los desarrolladores sobre sus preferencias de nomenclatura, lo más probable es que obtengas muchas opiniones diferentes. Sin embargo, puede haber un patrón similar al de los nombres anteriores: todos los nombres de columnas, cabeceras y pies de página se basan en el nombre del cuadro de lista. Estos nombres pueden ser asignados por una macro del formulario.
Inicio rápido
Las macros de formulario están disponibles a través del menú contextual del diseñador de formularios:
Si no ha utilizado macros de formulario antes, es probable que su menú contextual no incluya ningún menú de macros:
En este caso, lo primero que debes hacer es crear un archivo formMacros.json y colocarlo en la carpeta Sources de tu proyecto:
El archivo formMacros.json define los nombres de los elementos del menú y las clases que implementan el código. El archivo que estoy usando en este ejemplo es muy simple:
{ "macros": { "Rename Listbox columns": { "class": "RenameListboxColumns" } } }
Le dice a 4D que añada un elemento de menú llamado «Renombrar columnas de Listbox» al menú de Macros (mostrado en el menú contextual del diseñador de formularios). Cuando haga clic en el elemento de menú, llamará al método onInvoke de una clase llamada «RenameListboxColumns» (más sobre esto en breve). El archivo formMacros.json se lee una vez cuando se carga el proyecto. Los cambios en el contenido no se aplican a la interfaz de usuario hasta que se vuelve a cargar el proyecto. Si has creado el archivo formMacros.json en este momento, cierra tu proyecto y ábrelo de nuevo para ver el menú de Macros en el diseñador de formularios. Puedes encontrar información detallada sobre las macros de formulario aquí.
Las macros de formulario son clases
La implementación de una macro de formulario está dentro de una clase. La propiedad «class» en la definición de la macro («class»: «RenameListboxColumns») define su nombre. Para que nuestra macro haga algo, necesitamos crear esa clase:
Para ver si funciona, añade el siguiente código
// Macro invoked in the form designer.
Function onInvoke($editor: Object)->$return: Object
ALERT ("¡Hola mundo!") // Return the modified page object
$return :=New object("currentPage"; $editor.editor)
Al seleccionar la opción «Renombrar columnas de Listbox» en el menú de Macros, debería aparecer la alerta:
Esta es una comprobación básica para verificar que todo está configurado correctamente. Para mostrar una posible trampa, cambie el código a
// Macro invoked in the form designer.
Function onInvoke showHelloWorldMessage() ($editor: Object)->$return: Object
This .showHelloWorldMessage() // Return the modified page object
$return: =New object("currentPage"; $editor.editor)
Function
ALERT ("¡Hola mundo!")
Seleccione de nuevo el menú «Renombrar columnas de Listbox»:
4D carga la definición de la clase sólo una vez. Puede cambiar la implementación de los métodos de la clase, pero los nuevos métodos o los cambios en los parámetros de los métodos no estarán disponibles hasta que recargue el proyecto. Esto es un poco incómodo, pero se acostumbra a ello. Después de recargar el proyecto, el código funcionará bien.
Añadir funcionalidad
Ahora que ya sabes cómo configurar una macro de formulario, ¡vamos a añadir algo de funcionalidad! Mostrar un mensaje de Hola Mundo está bien, pero tendría sentido que una macro llamada «Renombrar columnas de Listbox» hiciera algo más. Por ejemplo, renombrar las columnas.
El método onInvoke pasa un objeto llamado $editor. Este objeto contiene información sobre el formulario en el que está trabajando actualmente. currentSelection ($editor.editor.currentSelection) contiene los nombres de los objetos seleccionados. Si no ha seleccionado ninguno, estará vacío.
currentPage ($editor.editor.currentPage ) contiene todos los objetos que se encuentran en la página del formulario actual. form ($editor.editor.form) contiene todo el formulario. Estos objetos son simplemente partes de la declaración JSON de los formularios. Si quieres tener una visión general de las estructuras de los objetos, simplemente abre el archivo form.4DForm en el que estás trabajando en un editor de texto.
Como queremos renombrar los nombres de los objetos de las columnas del cuadro de lista, los encabezados de las columnas y los pies de página, la lógica principal de la aplicación será
- Haga un bucle a través de los objetos seleccionados. Para cada cuadro de lista encontrado, haga lo siguiente:
- Recorrer las columnas del cuadro de lista y calcular los nombres de los objetos de cada columna, cabecera de columna y pie de página basándose en un patrón específico.
- Si uno de los nombres de objeto calculados ya se utiliza en el formulario, entonces aborta el proceso de renombramiento y muestra un mensaje de error al usuario.
- En caso contrario, cambie el nombre del objeto.
Comencemos con el primer tema:
- Hacer un bucle a través de los objetos seleccionados. Para cada cuadro de lista encontrado, haga los pasos descritos anteriormente:
// Macro invoked in the form designer.
Function onInvoke($editor: Object)->$return: Object
var $objectName : Text
var $formObject : Object
This .editor:=$editor.editor
If (This.editor.currentSelection.length>0)
For each ($objectName; Este.editor.editor)
$formObject :=This.editor.currentPage.objects[$objectName]
If (This.isListbox($formObject))
This .renameListboxColumns($objectName; $formObject)
End if
End for each
End if
// Return the modified page object
$return :=New object("currentPage"; This.editor.currentPage)
Este código coincide con la frase (bucle a través de los objetos seleccionados, comprobar los cuadros de lista y renombrar sus columnas). ¿Pero cómo identificamos un cuadro de lista? Pongamos un punto de interrupción en el código e investiguemos:
Cada objeto tiene una propiedad type. Para un cuadro de lista, se establece como «listbox». Esto es lo mismo que se ve al abrir el archivo form.4dform en un editor de texto:
// Check if the form object is a listbox
Function isListbox($formObject: Object)->$isListbox: Boolean
$isListbox :=($formObject.type="listbox")
¡Muy fácil! Por supuesto, este one-liner podría incluirse directamente en onInvoke en lugar de declarar un nuevo método, pero después de unos 30 años de desarrollo orientado a objetos, estoy acostumbrado a crear pequeños métodos. Ayuda a escribir un código fácil de entender y de mantener.
Ahora que sabemos cómo identificar un cuadro de lista, hagamos el siguiente paso:
- Recorrer las columnas del cuadro de lista y calcular los nombres de los objetos de cada columna, de la cabecera de la columna y del pie de página basándonos en un patrón 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 . ( . ; . +"Cabecera") := +1setObjectName$colheader $colname
$index$index
End for each
El cambio de nombre de un objeto no debe dar lugar a la duplicación de los nombres de los objetos.
- Si uno de los nombres de objeto calculados ya se utiliza en el formulario, entonces aborta el proceso de renombramiento y muestra un mensaje de error al usuario.
- En caso contrario, cambie el nombre del objeto.
// 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( : ; : ) ( # . ) ( . ( . . ; )) ("Nombre de objeto " +" ya utilizado. Cambio de nombre cancelado.") $formObject Object $newObjectName Text
If$newObjectName$formObjectname
IfThisisObjectNameUsedInFormThiseditorform $newObjectName
ALERT$newObjectName
ABORT // abort further processing
Else
$formObject. :=name$newObjectName
End if
End if
La comprobación de un nombre de objeto existente se realiza con algunos métodos más: isObjectNameUsedInForm, isObjectNameUsedInPage, y isObjectNameUsedInListbox. Están incluidos en el ejemplo final disponible en Github. También contiene un formulario que puedes utilizar para probar la macro:
Uso de las macros de formulario en tu trabajo diario
Si la implementación de una macro de formulario es específica para un solo proyecto, entonces tiene todo el sentido del mundo añadirla directamente a ese proyecto. Pero las macros como ésta pueden ser útiles en cualquier proyecto que contenga formularios. Puedes, por supuesto, copiar la clase a cada proyecto y añadir la definición de la macro a cada archivo formMacros.json, pero es más fácil crear un componente en su lugar.
Un componente de macro formulario no es más que un proyecto 4D con un archivo formMacros.json y las clases definidas en él. La única diferencia es que usted construye un componente y lo coloca en la carpeta de componentes apropiada de su proyecto, o lo coloca en la carpeta de componentes de la aplicación 4D para tenerlo disponible en todos los proyectos por defecto.
Si sólo quiere usar la macro y no le importa la implementación: el ejemplo en GitHub incluye el componente compilado en la carpeta Build.