<commit_before>---
layout: article
title: Validaciones y manejo de errores en la UI
categories: [arena, validaciones, errores]
featured: true
---

Podemos ver un repaso del tema excepciones en [esta página](excepciones-avanzadas.html)

# Introducción

Consideremos el ejemplo de los clientes de una empresa de celulares, donde tenemos un formulario que permite ingresar

- nombre del cliente
- número de celular
- modelo de celular
- si quiere recibir el resumen de cuenta en el domicilio

# Validaciones a implementar

1. El número debe contener sólo dígitos numéricos
1. El modelo de celular debe ser un modelo válido
1. Los números de celular deben ser mayores a 1000
1. No puede ingresarse el mismo número de teléfono para dos clientes diferentes
1. Algunos modelos de celular exigen que sus clientes reciban el resumen de cuenta en su domicilio

¿Qué hacemos en cada caso? ¿Quién es responsable de cada validación?

# Momentos de la validación

## El número debe contener sólo dígitos numéricos

Si el objeto de dominio Celular define el número de teléfono como un Integer, no es posible hacer

```scala
celular.numero = "A"
```

eso **no compila**. Pero la UI podría tener un cuadro de texto que permita ingresar caracteres alfanuméricos: entonces tenemos que elegir cuál va a ser el comportamiento del sistema

1. permitir ingresar caracteres inválidos pero mostrar un mensaje de error: en Arena esto lo hace por defecto el controller que adapta lo que el usuario carga a lo que el dominio necesita. El panel de errores (ErrorsPanel) captura cualquier excepción que ocurra en la conversión, ya sea que lo incluyamos manualmente o por una ventana que herede de `SimpleWindow`.
1. podríamos pensar: ¿para qué dejamos que el usuario ingrese un caracter inválido si luego lo vamos a rechazar? La segunda variante consiste en definir un filtro que no permita que el usuario pueda ingresar caracteres alfabéticos si queremos que ingrese números. Para esto...

### Opción 1: Definimos un filter

Esta es la versión en lenguaje Java:

```java
new TextBox(form)
    .withFilter(new TextFilter {
        public boolean accept(TextInputEvent event) {
            return StringUtils.isNumeric(event.getPotentialTextResult());
        }})
    .bindValueToProperty(Celular.NUMERO);
```

La misma versión en lenguaje Xtend:

```scala
new TextBox(form) => [
    withFilter [ event | event.potentialTextResult.matches("[0-9,.]*") ]
    bindValueToProperty("numero")
    width = 100
]
```

¿Qué es el TextFilter dentro del MVC? El TextFilter es un **controller**, porque se comunica con el dominio _(le manda un mensaje al modelo - en el caso de estar ok el input)_ y actúa sobre la vista _(en la pantalla puede no aparecer ese caracter)_.

### Opción 2: Utilizamos un control específico para ingresar números

Desde Arena 3.6.1 tenemos un control NumericField que se encarga de filtrar los caracteres alfabéticos:

```java
// Java
new NumericField(form).setWidth(150).bindValueToProperty("numero");
```

```scala
// Xtend
new NumericField(form) => [
    value <=> "numero"
    width = 100
]
```

Internamente está utilizando un Filter, lo importante es que tenemos una abstracción de alto nivel que está diciendo "quiero un control donde sólo se puedan cargar números".

Entonces la primera variante es impedir cualquier ingreso inválido por parte del usuario: Esto tiene como ventaja ser fail fast, evita ingresos incorrectos y esto para el usuario es más beneficioso. 

## Otras formas de evitar acciones incorrectas

Siguiendo la anterior linea de pensamiento, ¿debería dejar que el usuario presione el botón Aceptar, solo para mostarle un mensaje de error después? ¿no debería habilitar el botón Aceptar solamente cuando todos los controles se hayan cumplido satisfactoriamente?

Aquí vemos que esta estrategia tiene un límite: en ciertos casos las validaciones del negocio pueden ser verdaderamente complejas como para poder dejarlas en forma explícita en la pantalla. Entonces el usuario sentirá una lógica frustración de no poder avanzar con el caso de uso cuando el botón Aceptar esté inhabilitado y no quede claro por qué. **Una regla importante para la usabilidad de un sistema es que debe explicar claramente al usuario qué información no cumple las reglas de negocio y además cómo debe continuar para llegar al caso exitoso**.

# Implementando las validaciones restantes

## El modelo de celular debe ser un modelo válido

¿El responsable es el celular? sí, si lo pensamos como un dato obligatorio, pero claramente participan

1. la UI que guía al usuario mostrándole un combo con las opciones válidas, el usuario no puede elegir un modelo inexistente, a lo sumo puede dejarlo vacío
1. si yo permito que el combo quede vacío, el dominio (el objeto celular) debería validar que el celular no deje en blanco el campo modelo. El form builder permite en sus opciones decirle "este combo no tiene la opción vacía".

```java
// Java
Selector<ModeloCelular> selector = new Selector<ModeloCelular>(form) //
     .allowNull(false);
```<commit_msg>Error handling enhancements<commit_after>---
layout: article
title: Validaciones y manejo de errores en la UI
categories: [arena, validaciones, errores]
featured: true
---

Podemos ver un repaso del tema excepciones en [esta página](excepciones-avanzadas.html)

# Introducción

Consideremos el ejemplo de los clientes de una empresa de celulares, donde tenemos un formulario que permite ingresar

- nombre del cliente
- número de celular
- modelo de celular
- si quiere recibir el resumen de cuenta en el domicilio

# Validaciones a implementar

1. El número debe contener sólo dígitos numéricos
1. El modelo de celular debe ser un modelo válido
1. Los números de celular deben ser mayores a 1000
1. No puede ingresarse el mismo número de teléfono para dos clientes diferentes
1. Algunos modelos de celular exigen que sus clientes reciban el resumen de cuenta en su domicilio

¿Qué hacemos en cada caso? ¿Quién es responsable de cada validación?

# Momentos de la validación

## El número debe contener sólo dígitos numéricos

Si el objeto de dominio Celular define el número de teléfono como un Integer, no es posible hacer

```scala
celular.numero = "A"
```

eso **no compila**. Pero la UI podría tener un cuadro de texto que permita ingresar caracteres alfanuméricos: entonces tenemos que elegir cuál va a ser el comportamiento del sistema

1. permitir ingresar caracteres inválidos pero mostrar un mensaje de error: en Arena esto lo hace por defecto el controller que adapta lo que el usuario carga a lo que el dominio necesita. El panel de errores (ErrorsPanel) captura cualquier excepción que ocurra en la conversión, ya sea que lo incluyamos manualmente o por una ventana que herede de `SimpleWindow`.
1. podríamos pensar: ¿para qué dejamos que el usuario ingrese un caracter inválido si luego lo vamos a rechazar? La segunda variante consiste en definir un filtro que no permita que el usuario pueda ingresar caracteres alfabéticos si queremos que ingrese números. Para esto...

### Opción 1: Definimos un filter

Esta es la versión en lenguaje Java:

```java
new TextBox(form)
    .withFilter(new TextFilter {
        public boolean accept(TextInputEvent event) {
            return StringUtils.isNumeric(event.getPotentialTextResult());
        }
    })
    .bindValueToProperty(Celular.NUMERO);
```

La misma versión en lenguaje Xtend:

```scala
new TextBox(form) => [
    withFilter [ event | event.potentialTextResult.matches("[0-9,.]*") ]
    bindValueToProperty("numero")
    width = 100
]
```

¿Qué es el TextFilter dentro del MVC? El TextFilter es un **controller**, porque se comunica con el dominio _(le manda un mensaje al modelo - en el caso de estar ok el input)_ y actúa sobre la vista _(en la pantalla puede no aparecer ese caracter)_.

### Opción 2: Utilizamos un control específico para ingresar números

Desde Arena 3.6.1 tenemos un control NumericField que se encarga de filtrar los caracteres alfabéticos:

```java
// Java
new NumericField(form).setWidth(150).bindValueToProperty("numero");
```

```scala
// Xtend
new NumericField(form) => [
    value <=> "numero"
    width = 100
]
```

Internamente está utilizando un Filter, lo importante es que tenemos una abstracción de alto nivel que está diciendo "quiero un control donde sólo se puedan cargar números".

Entonces la primera variante es impedir cualquier ingreso inválido por parte del usuario: Esto tiene como ventaja ser fail fast, evita ingresos incorrectos y esto para el usuario es más beneficioso.

## Otras formas de evitar acciones incorrectas

Siguiendo la anterior linea de pensamiento, ¿debería dejar que el usuario presione el botón Aceptar, solo para mostarle un mensaje de error después? ¿no debería habilitar el botón Aceptar solamente cuando todos los controles se hayan cumplido satisfactoriamente?

Aquí vemos que esta estrategia tiene un límite: en ciertos casos las validaciones del negocio pueden ser verdaderamente complejas como para poder dejarlas en forma explícita en la pantalla. Entonces el usuario sentirá una lógica frustración de no poder avanzar con el caso de uso cuando el botón Aceptar esté inhabilitado y no quede claro por qué. **Una regla importante para la usabilidad de un sistema es que debe explicar claramente al usuario qué información no cumple las reglas de negocio y además cómo debe continuar para llegar al caso exitoso**.

# Implementando las validaciones restantes

## El modelo de celular debe ser un modelo válido

¿El responsable es el celular? sí, si lo pensamos como un dato obligatorio, pero claramente participan

1. la UI que guía al usuario mostrándole un combo con las opciones válidas, el usuario no puede elegir un modelo inexistente, a lo sumo puede dejarlo vacío
1. si yo permito que el combo quede vacío, el dominio (el objeto celular) debería validar que el celular no deje en blanco el campo modelo. El form builder permite en sus opciones decirle "este combo no tiene la opción vacía".

```java
// Java
Selector<ModeloCelular> selector = new Selector<ModeloCelular>(form) //
     .allowNull(false);
```
{'fraction_non_alphanumeric': 0.05228758169934641, 'fraction_numerical': 0.005743711626064567, 'mean_word_length': 4.485605649103748, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '366317', 'n_tokens_mistral': 3149, 'n_tokens_neox': 3129, 'n_words': 1444}