Implementación de una pantalla de búsqueda en Arena

Ayuda para entender cómo funciona un panel de búsqueda

Un panel de búsqueda sirve para mostrar la información que el usuario cargó previamente y permite crear una nueva entidad llamando a la pantalla de edición en modo Alta.

En amarillo, los packages que tenés que implementar en tu aplicación. La interfaz Home<T> se reemplazó por Repo<T>.

Vista de búsqueda

  • Extiende de SimpleWindow<T>, aunque vos podés diseñar tu propia SearchWindow como en el ejemplo del videoclub
  • Su model tiene que estar anotado como @Observable, para que dispare notificaciones ante un eventual cambio

Define

  • en el constructor, el modelo (y opcionalmente puede disparar la búsqueda)
  • en el createMainTemplate, el título, debe invocar a super.createMainTemplate (que define un formulario de default que nos sirve para ingresar un criterio de búsqueda) y agregar dos métodos para definir la grilla de resultados y las acciones que le corresponden a cada elemento encontrado
  • en el createFormPanel, el layout y los controles que van a aparecer. Recordemos que cada control que ingresa información debe bindearse contra una propiedad. Esto puede ser:
    • una propiedad directa del model (como en el ejemplo de los celulares)
    • la propiedad de un objeto example que contenga el model (como en el ejemplo del videoclub). Esto se logra porque el formPanel (el que estamos definiendo ahora) tiene como model un objeto de dominio que sirve a su vez de ejemplo para la búsqueda, y el application model tiene como propiedad un example.
  • en el addActions, las acciones que puede disparar el usuario asociado al formulario que creamos en el createFormPanel. Esto en principio es Buscar, Limpiar y Nuevo, pueden agregarse más.
  • en el createResultsGrid, defino los controles que estarán asociados a los resultados de la búsqueda. En principio lo modelamos con una grilla, pero podríamos usar cualquier otro control que deseemos. La grilla debe bindearse contra la propiedad results del modelo, de manera que cuando el repo actualice el modelo de la vista (el app model), se notifique a la vista el cambio y la grilla se actualice sola. Cada columna de la grilla observa una propiedad de cada elemento del result. Entonces, si results es una lista de T, las propiedades de cada columna tienen que ser una propiedad de T, no del modelo de la vista. Pasado en limpio,
    • si tu panel tiene como modelo un BuscadorCelular,
    • la grilla tiene como modelo un List<Celular>.
    • A su vez, la columna "Número" está bindeada contra la propiedad Celular.NUMERO (el string "Numero"), no contra el BuscadorCelular.
    • Algunos elementos de la grilla requerirán un adapter, para transformar un date / número / objeto de dominio en un String (esto se hace bindeando la columna a un Transformer que se implementa con un closure)
  • en el createGridActions hay que escribir las acciones que cada elemento de la grilla puede disparar, en principio Editar y Borrar.
    • cuando el usuario selecciona un elemento de la grilla se habilitan las acciones anteriores
    • si la acción requiere que el usuario haya seleccionado un elemento previamente, esto se define haciendo un binding de la propiedad enabled contra el elemento selected del modelo. Esto permite que no puedas editar o borrar un celular si la grilla está vacía o no seleccionaste ningún elemento
  • Además hay que definir los métodos que son responsabilidad de la vista, en este caso
    • el alta
    • y la edición de un objeto, porque abren la ventana de edición en modo alta/edición
    • El alta y la edición no están asociados al modelo, porque es responsabilidad de la vista, que debe:
      • abrir una ventana de diálogo de edición
      • y luego disparar la búsqueda, para hacer que se actualicen los resultados de la grilla con la información que el usuario haya cargado

Modelo de la vista de búsqueda (application model)

  • Como dijimos antes, la clase debe estar anotada como @Observable como todo modelo de una vista.
  • El criterio de búsqueda puede ser "by example" o específico (definido por algún objeto al que le podamos pedir que busque, podría incluso ser un objeto de negocio)
  • No siempre es posible implementar una búsqueda by example:
    • si queremos ingresar un rango de fechas para ver los clientes que se dieron de alta en este año,
    • o si queremos ver los clientes de Catamarca, La Rioja y Tucumán (una lista de provincias, cuando el cliente tiene una sola provincia asociada),
    • o si queremos excluir los clientes morosos (cuando almacenamos la deuda o la calculamos mediante sus pagos pendientes) tendremos que armar un application model específico
  • Si la búsqueda es by example, las opciones son:
    • un objeto especial que sirve como application model (como en el caso de los celulares, es la clase BuscadorCelular)
    • o un objeto que es instancia de SearchByExample<T> (si implementamos la búsqueda by example) o Search<T> (en caso contrario) donde T es el objeto de dominio de lo que queremos buscar.

Objeto application model explícitamente definido

  • Hereda de Search<T> o SearchByExample<T>
  • Conoce al criterio de búsqueda
  • El criterio de búsqueda tiene que publicar su par de getters/setters (si se trabaja con atributos separados)
  • Puede redefinir el limpiar default en el método clear
  • Puede redefinir la búsqueda default en el método doSearch
  • Puede redefinir el eliminar elemento seleccionado default en el método removeSelected

Qué hace un Search<T>

  • Conoce un List<T> de results, donde T son los objetos de dominio (socios, celulares, alumnos, etc.)
  • Sabe qué elemento seleccionó el usuario en la grilla (es obviamente un T selected)
  • Define un método search que dispara la búsqueda (el que se puede invocar con el botón "Buscar")
  • El search es un template method que delega la responsabilidad explícita de buscar al doSearch (si tu application model hereda de Search<T> tenés que definirlo, si tu application model hereda de SearchByExample<T> podés heredar el comportamiento default o redefinirlo)
  • No define el Nuevo ni el Editar, esas son responsabilidades de la vista

Qué hace un SearchByExample<T>

  • Hereda de Search<T>, por lo que toma por default su comportamiento
  • Conoce un example para implementar la búsqueda, sabe crearlo y publica un getter
  • Define el método clear por default (el que es invocado por el botón "Limpiar")
  • Define el método removeSelected (el que se puede invocar con el botón "Borrar" al seleccionar un elemento de la grilla)

Responsabilidades del Repo (ex-Home)

  • Sabe hacer la búsqueda
    • by example: searchByExample(T example)
    • o común
  • Sabe eliminar un elemento: delete(T object)
  • Y para la ventana de edición:
    • Sabe actualizar un elemento T: update(T object)
    • Sabe insertar un elemento T: create(T object)