Celulares en Wicket
Detalles de la solución presentada
Objetos que no dependen de la tecnología de presentación (comunes para cualquier proyecto)
Los siguientes objetos se importaron del proyecto de Celulares resuelto en Arena (ver página de ejemplos).
Objetos de dominio:
- Celular: abstracción que representa un cliente de una compañía de celulares
- ModeloCelular: abstracción que representa un modelo de celular de un cliente
Objetos home:
- RepositorioModelos: instancia y permite conocer todos los modelos de celular que la aplicación conoce
- RepositorioCelulares: instancia el conjunto de clientes de la compañía / hace la búsqueda de clientes / valida si un número de celular está asignado a otro cliente
Objetos de modelo de vista/application model:
- BuscadorCelular: recolecta la información del panel de búsqueda y delega en el home (RepositorioCelulares) la búsqueda by example / también sabe limpiar los campos de búsqueda nombre y número de celular
Objetos definidos para proyecto wicket (dependientes de la tecnología)
Pantalla principal: búsqueda de celulares
La pantalla tiene el formato:
Controles y sus mapeos:
(*) Hay que tener cuidado, originalmente en el html el control checkbox de la grilla estaba disabled=true. Esto tiene como efecto colateral que al editar un cliente el form de Wicket no refresca ese dato en el model (entonces se pierde el dato de si el cliente quiere recibir el resumen de cuenta en domicilio). La solución es deshabilitar el control desde java, mediante un:
val checkResumen = new CheckBox("recibeResumenCuenta") checkResumen.setEnabled(false)
(**) Al generar el formulario se dispara la búsqueda, entonces al cargar la pantalla se muestran los datos de los clientes existentes,también cuando se vuelve de la pantalla de edición/nuevo cliente. BONUS: Ver cómo cambiar el flujo si lo que se quiere es no hacer la búsqueda al comenzar el formulario.
Decisiones más relevantes:
- Pasaje de info entre pantallas: el panel de búsqueda le pasa al panel de edición cuál es el cliente que quiere modificar, a través de un constructor explícito (igual que en Arena). Esto se ve en el método actualizar del panel de búsqueda, común para edición y nuevo cliente:
def editar(Celular celular) { responsePage = new EditarCelularPage(celular, this) }
Pantalla de Edición de datos
El formato es el siguiente:
Controles y sus mapeos:
Decisiones más relevantes:
- Uso del combo: se define un DropDownChoice<ModeloCelular>, cada opción va a ser un ModeloCelular posible. Propiedades del combo:
- id: modeloCelular
- modelo asociado: la lista de modelos disponibles, que lo resuelve el home; sólo tenemos que decirle
- cómo obtener esa lista en el momento de cargar el combo (LoadableModel)
- cómo vamos a renderizar cada opción, es decir, qué vamos a mostrar para cada ítem (a través de un ChoiceRenderer)
parent.addChild(new DropDownChoice<Modelo>("modeloCelular") => [ choices = loadableModel([| Modelo.home.allInstances ]) choiceRenderer = choiceRenderer([Modelo m| m.descripcion ]) ])
El choice renderer define que se va a mostrar en cada opción del combo la descripción de cada modelo de celular, esto podríamos reemplazarlo por cualquier otro mensaje que queramos.
Al cargarse el panel en modo Alta, el modelo de un celular nuevo referencia a null, no obstante el fwk se da cuenta y deja el combo sin selección.
Al cargarse el panel en modo Edición, el fwk se encarga de matchear el modelo existente con la lista de opciones posibles y bindea automáticamente la selección del combo.
- Botón Aceptar: el submit del botón delega
- la validación al objeto Celular, si ocurre una excepción de negocio se maneja enviando el mensaje info al feedbackPanel del formulario. El objeto Celular ya tiene toda la info cargada por el usuario (el binding es automático)
- el agregado/actualización de los datos se delega al home (también puede ocurrir una excepción de negocio, se trata como los demás)
- si ocurre un error de programa/sistema no mostramos el error, sino que avisamos al usuario que hubo un error (TODO: guardar el error en un archivo de log que pueda ser leído posteriormente por alguien técnico)
parent.addChild(new XButton("aceptar") => [ onClick = [| try { celular.validar() if (alta) { Celular.home.create(celular) } else { Celular.home.update(celular) } volver() } catch (UserException e) { info(e.getMessage()) } catch (RuntimeException e) { error("Ocurrió un error al procesar el pedido del celular. Consulte al administrador del sistema") } ] ])
- Navegación a la pantalla principal: el método volver se encarga de esto:
def volver() { mainPage.buscarCelulares() responsePage = mainPage }
El mainPage lo recibimos al construir la página de edición:
new(Celular celularAEditar, BusquedaCelularesPage mainPage) { this.mainPage = mainPage ...
De esta manera se conserva la información de la pantalla de búsqueda (por ser stateful).