# Patrón PRG y redirección

[← Volver al índice](../crudl.md)

La implementación del CRUD-L con interfaz de usuario Web realiza las acciones de adición y edición siguiendo el patrón <a href="https://es.wikipedia.org/wiki/Post/Redirect/Get" target="_blank">PRG (Post - Redirect - Get)</a>, que evita que una recarga del navegador reenvíe el formulario.

<img src="../img/prg-pattern.svg">

## El ciclo

1. **GET** a `.../{tipo}/_new` o a `.../{tipo}/{id}` con `Accept: text/html` devuelve el formulario, vacío o con los datos de la entidad.
2. **POST** al mismo punto final envía los datos. El controlador realiza el alta o la modificación.
3. En caso de éxito se responde con una **redirección** (encabezado `Location`).
4. El navegador realiza un **GET** a la URL indicada, que normalmente es la lista.

Siempre debe establecerse el encabezado `Accept: text/html` para que la respuesta al POST envíe la redirección en lugar de los datos en JSON de la entidad creada o actualizada.

## Alta y edición con el mismo formulario

Para solicitar el formulario vacío se realiza un GET con `_entity_id=_new`; para obtener el formulario con los datos de una entidad se indica su identificador.

El valor del identificador para adición puede establecerse mediante `@entity_id_blank`, cuyo valor predeterminado es `_new`.

Al enviar el formulario:

* `POST .../{tipo}/_new` produce un alta.
* `POST .../{tipo}/{id}` produce una modificación, porque el controlador reescribe el método como PATCH.

De este modo la misma vista y el mismo punto final atienden ambos casos, y el formulario puede enviarse siempre a la URL actual.

## Destino de la redirección

El destino se determina por el primer criterio que aplique:

1. Si ya se estableció un encabezado `Location` en la respuesta, no se modifica.
2. Si `@url_redir` tiene valor, se evalúa como plantilla y se usa el resultado.
3. Si se recibió el parámetro de retorno `---from_url`, se usa su valor.
4. En cualquier otro caso, se compone una URL predeterminada.

### URL predeterminada

Se toma la URI de la solicitud sin la cadena de consulta, se retira el segmento del identificador si lo había, y se agregan dos parámetros:

```
.../{tipo}/?_select={identificador}&_key={campo_clave}
```

El parámetro `_select` transporta el identificador del registro recién guardado. Su vista de lista puede usarlo para resaltar, desplazar o seleccionar esa fila:

```
##
seleccionado = @@(@crud_context,"$parameters/_select")
##
```

`_key` indica el campo con el que se debe interpretar `_select`.

### Parámetro de retorno

Para que la redirección devuelva al usuario al lugar del que vino, por ejemplo a una lista con filtros aplicados, envíe la URL de retorno en el formulario.

El campo debe llamarse `---from_url`. El prefijo `---` indica al controlador que se trata de un campo de control y no de un dato de la entidad, por lo que se retira de la carga útil antes de que llegue al modelo.

```html
<input type="hidden" name="---from_url" value="/misistema/cliente/?estado=activo">
```

Si necesita transportar la URL codificada, use `---from_url_e` o `_from_url_e`; el controlador la decodificará.

Desde una vista de lista, lo natural es componerlo con la URI actual:

```
##
retorno = @@(@crud_context,"http/request/headers/REQUEST_URI")
##
input(type="hidden" name="---from_url" value=$"#<retorno>")
```

### Plantilla de redirección

`@url_redir` permite construir el destino a partir de los datos de la operación. Se evalúa como texto con formato en un contexto que contiene los mismos miembros que una vista: `output`, `input`, `parameters`, `error`, `database` y `http`.

```
@url_redir = "/misistema/factura/#<@@(@crud_context,'$output/sys_pk')>/impresion"
```

Establézcala en el `controller.dk` de la entidad cuando el destino dependa del resultado de la operación.

## Campos de control

Todo campo de la carga útil cuyo nombre comience con `---` se mueve a los parámetros de la solicitud antes de que el modelo reciba los datos.

Esto permite que un formulario HTML transporte información que gobierna el comportamiento del controlador sin contaminar la entidad. El marco usa este mecanismo para `---from_url`, pero puede emplearlo para sus propios fines: cualquier campo con ese prefijo estará disponible en `parameters` y no en `input`.

```html
<input type="hidden" name="---sucursal_origen" value="7">
```

```
##
origen = @@(@crud_context,"$parameters/---sucursal_origen")
##
```

## Suprimir la redirección

El parámetro `_redirect=disabled` sustituye la redirección por el renderizado de la vista de formulario. Aplica a las operaciones CREATE, UPDATE y DELETE.

```html
<form method="POST" action="?_redirect=disabled">
```

Es útil cuando desea permanecer en el formulario tras guardar, por ejemplo en una captura continua, o cuando el formulario se muestra dentro de un elemento de diálogo y no conviene navegar.

Con la redirección suprimida, la vista de formulario recibe en `output` la entidad recién guardada, releída y completa, incluidos los campos de sistema actualizados.

## Baja desde la interfaz Web

El navegador no puede emitir `DELETE` desde un formulario estándar. Para dar de baja desde una interfaz Web dispone de dos caminos:

* Emitir la solicitud `DELETE` con JavaScript y atender la respuesta en el cliente.
* Publicar un punto final propio en el `controller.dk` de la entidad que reciba un POST y realice la baja.

## Errores en el ciclo PRG

Si el alta o la modificación fallan, no hay redirección: se renderiza la vista de formulario con el objeto de error y con `output` sustituido por los datos que el usuario había enviado. Ver [Implementación de las vistas](vistas.md#sustitución-de-output-por-input-en-caso-de-error).

Esto significa que el formulario debe estar preparado para mostrar `error/message` cuando no sea nulo:

```
##
ref e = @@(@crud_context,"&error")
if not(isnull(e))
{
    ##
    div(class="alert alert-danger"){ $"#<@@(e,'$message')>" }
    ##
}
##
```

## Documentos relacionados

* [El controlador](controlador.md)
* [Implementación de las vistas](vistas.md)
* [Referencia de parámetros y variables](referencia.md)
