Como ejecutar un evento al terminar de escribir en un campo en Genexus

 Algunas veces al desarrollar una aplicación, nos encontramos con la necesidad de realizar un cálculo cuando se modifica algún campo. Para esto en Genexus existen dos tipos de eventos que nos ayudan a ejecutar un codigo despues de que se modificó un campo, ControlValueChanged y ControlValueChanging.

Pero este artículo no es sobre como utilizar estos eventos, si no más bien de como crear un nuevo evento que nos ayude a realizar una función que los otros dos eventos no pueden realizar.

Para que se comprenda lo que se quiere lograr, vamos a explicar como funcionan a grandes rasgos los otros dos eventos:

ControlValueChanged. Este evento se ejecuta cuando se detecta un cambio sobre un campo. Para los campos de texto, es necesario salir del campo para que este evento se ejecute.

ControlValueChanging. Este evento se ejecuta cuando se detecta un cambio sobre un campo. Para los campos de texto, se ejecuta cada vez que se presiona una tecla sobre el campo, por ejemplo al escribir "Hola" el evento se ejecutaría 4 veces. 

Bien, ahora veamos un ejemplo práctico. Vamos a crear un aplicación en la que se ingrese la estatura y el peso, y nos muestre el IMC. Esta calculadora no tendrá un botón para calcular, así que utilizaremos el evento ControlValueChanging, para que al modificar el peso nos realice el calculo:






Como ven es algo muy sencillo, quizás se pregunten por que muestro de nuevo la estatura y el peso que se ingreso si ya se puede ver en los campos de arriba, bueno esto es algo que nos ayudara a notar el problema que tiene el evento ControlValueChanging.


Vamos a hacer una prueba, ingreso unos datos y nos muestra un resultado correcto:



Funciona bien, pero qué pasa si tenemos la necesidad de que en el evento se recupere información de la base de datos para realizar los cálculos?, pues cada vez que escribamos sobre el campo se va a realizar un llamado a la base de datos. Para simular esto voy a poner un sleep, que simule el tiempo que le tomaría al evento terminar de ejecutarse:



Al ingresar los mismos datos de prueba, vemos que el resultado es diferente:




Si nos fijamos en el dato de Peso que esta arriba de IMC, podemos ver que esta tomando el valor de 10, pero nosotros ingresamos 100, por que el evento no tomo la cantidad correcta?, Esto es por que cada cifra que se escribe ejecuta el evento y debido que el evento le esta tomando mas tiempo ejecutarse no esta respondiendo a los últimos números ingresados.


Como podemos solucionar esto? podríamos utilizar el evento ControlValueChanged, que solo se ejecuta una vez, pero el problema que solo se ejecutara hasta que salgas del campo, lo cual podría ser confuso para el usuario.


Nuevo Evento

Así que veamos que necesitamos, un campo que ejecute un evento pero solo una vez, cuando terminemos de escribir y sin que sea necesario salir del campo. Bien, eso es lo que va a hacer nuestro nuevo evento.

Para hacerlo vamos a utilizar un poco de código Javascript. Primero crearemos un external object llamado Calcular, y en su propiedad "Javascript External Name" pondremos Calcular



Agregamos un evento llamado IMC en la sección Events, y en su propiedad "Javascript External Name" podremos Calcular.IMC


Esto es lo único que tenemos que hacer en el external object, ahora volvemos a nuestro webpanel y primero vamos a agregar un TextBlock en nuestro form, y modificamos su propiedad Format a HTML. Cambiaremos el nombre del textblock a Script.



En el código vamos a agregar el evento Start, y vamos a crear una variable Script (LongVarchar), en la que escribiremos el siguiente código:



Básicamente lo que hace ese código es asignarle un evento al campo Peso, que se ejecuta cada vez que se presiona una tecla sobre el campo, el evento crea un temporizador de 1 segundo y al terminar ejecuta el evento de genexus llamado Calcular.IMC, Si se sigue escribiendo antes de que el temporizador termine resetea el temporizador y vuelve a iniciar, de esta manera nos aseguramos de que el evento solo se ejecute 1 segundo después de que se terminó de escribir.

Ahora lo que haremos es copiar el código que teníamos en el evento &Peso.ControlValueChanging, y lo pasaremos al evento Calcular.IMC. Algo importante es no borrar y mantener el evento &Peso.ControlValueChanging. El codigo quedaria asi:



Volvemos a ingresar los datos para hacer una prueba, y como se puede observar el resultado es correcto, ahora el evento solo se ejecuta al terminar de escribir y solo una vez.






Como crear un campo editable con un custom suggest en #Genexus

 Todos conocemos que en genexus existe la propiedad Suggest para los campos edit, el cual permite que cuando se ingrese información en el campo te muestra sugerencias que carga desde un catálogo para que puedas seleccionar, pero que pasa si lo que quieres es tener un campo en el que puedas escribir, te muestre sugerencias y en caso de que lo que ingresaste no exista en el catálogo, lo puedas agregar, esto no lo permite el campo con propiedad suggest.

Justamente eso es lo que me sucedió cuando en una sección donde tenía que agregar Habilidades, quería mostrar un campo en el que se escribiera la habilidad y en caso de que ya existiera te mostrará sugerencias del catálogo, pero si no existía te permitiera agregarla como nueva, pero me lleve la sorpresa de que el suggest no permite que obtengas el valor escrito si no existe en una tabla, asi que cree mi propio suggest, que es lo que a continuación les mostraré.


Custom suggest en #Genexus

    1. Creamos una transacción de ejemplo que usaremos como catálogo que alimentará nuestro suggest custom:


    2. Creamos un nuevo webpanel :



    2. En el web form vamos a agregar una tabla con una variable (&editHabilidadNombre Varchar 200) y una imagen que sera el botón para agregar:


    3. Agregamos otra tabla que llamaremos TblGridHabilidadSuggest y dentro agregamos un grid que llamaremos GridHabilidadSuggest que tendra las variables &gHabilidadNombre y &gHabilidadId


    4. En las propiedades del grid, vamos a activar la propiedad Allow Selection:

    

    5. En el theme creamos una nueva clase table a la cual llamaremos TableGridSuggest y se la asignaremos a la tabla que contiene al grid, la cual debe tener las siguientes propiedades:

    

    

    6. En el Theme creamos una nueva clase del tipo GridRow a la cual llamaremos GridRowNoHeader y le pondremos la siguiente propiedad:


    7. En el Theme creamos una nueva clase del tipo Grid que llamaremos GridSinHeader y se la asignaremos al grid que creamos, tendra las siguiente propiedades :


    8. En la sección de eventos, en el start vamos a ocultar la tabla que contiene el grid:


    9. Agregamos otro evento que va a controlar cuando se este escribiendo sobre el campo y realizara un refresh sobre el grid:


    10. Creamos el evento load del grid, en el cual validamos si el campo no esta vacio, en ese caso recorremos con un for each el catalogo de habilidad y filtramos por las habilidades que inicien con lo que esta escrito en el campo hasta el momento, y volverá visible la tabla que contiene el grid:


    11. Creamos el evento OnLineActivate del grid, en el que vamos a llamar un procedimiento que nos guardara la habilidad seleccionada en el suggest, después vamos a limpiar el campo con setEmpty() y ocultamos la tabla que contiene al grid:



De esta forma tendremos un grid que funciona como un suggest y si lo que se ingreso en el campo no existe en nuestro catalogo tenemos la posibilidad de agregar directamente usando lo que se ingreso en el campo.


He omitido varias partes como el evento del boton para agregar, ya que este funciona como cualquier otro y no es importante para lo que se quiere mostrar. También omití que hay otro grid debajo en el cual se van mostrando las habilidades que ya estan agregadas y que se van agregando, pero no es necesario para realizar el custom suggest.


Seguramente habrá mejores formas para resolver esta necesidad o tengan otras ideas para mejorarlo y me encantará leerlos, así que si gustan pueden comentar aquí cualquier idea o duda que tengan.