# Implementación de las vistas

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

Las vistas se ejecutan en un contexto diferente al del programa principal que responde a la solicitud HTTP, por lo que reciben la información necesaria a través de la variable global `@crud_context`.

## El contexto de la vista

`@crud_context` tiene los siguientes miembros:

| Miembro | Descripción |
|---|---|
| `output` | Datos de salida, que generalmente serán pintados en el formulario o en la lista. Habitualmente, los campos del elemento de datos. |
| `.` | Alias de `output`. |
| `input` | Datos de entrada de la operación. Habitualmente, lo que se envió desde el formulario. |
| `parameters` | Parámetros de la solicitud, habitualmente los de la URL. |
| `error` | Nulo si todo ha ido bien, o una referencia a un objeto con información del error. |
| `database` | Referencia a la conexión de base de datos. |
| `http` | Referencia a todo el objeto `@http_context` de la solicitud en curso. |
| `script` | Contenido del archivo de script asociado a la vista, si se configuró uno. |

Además, `parameters` incluye siempre el campo `_operation` con el nombre de la operación realizada: `list`, `read`, `create`, `update` o `delete`.

### El objeto de error

Cuando la operación falla, `error` contiene un objeto con un solo campo:

```json
{ "message": "Texto del error" }
```

### Sustitución de output por input en caso de error

Cuando hay error y existen datos de entrada, **`output` se sustituye por `input`**.

Esto es lo que permite que un formulario que falla al guardar repinte los valores que el usuario había capturado, en lugar de volver a los almacenados. Escriba sus formularios leyendo siempre de `output`; el marco le entregará lo correcto en cada caso.

### La conexión está disponible

`database` permite que la vista consulte la base de datos, por ejemplo para llenar listas desplegables. Las utilidades `uie.dbSelect` y `uie.dbTable` están pensadas para esto. Ver [Ayudantes para generar HTML](../uielements.md).

## Resolución de la vista

El nombre de la vista se toma de `@list_view`, `@form_view` o `@error_view`, según la operación. Si el nombre no incluye extensión, se le agrega la indicada en `@dkl_view_ext` (predeterminado `.dk`).

El archivo se busca en cuatro ubicaciones, en este orden:

1. **La ruta declarada en el archivo `.crudl`** para esa entidad y ese nombre de vista. Ver [Archivo de configuración `.crudl`](configuracion.md).
2. **La carpeta de la entidad**: `{@path_root}/{@base_path}/{_entities_type}/`
3. **La ruta común de entidades**: `{@path_root}/{@base_path}/`
4. **La carpeta del marco**: `@dbmvc_path` (predeterminado `crudl/`)

Se usa la primera coincidencia. Si no se encuentra ninguna, se produce un error `500`.

Esta cadena permite tres niveles de reutilización: una vista propia por entidad, una vista común para todas las entidades del sistema, y la vista genérica del marco como último recurso.

Las vistas genéricas incluidas en `crudl/` producen una salida de diagnóstico, no una interfaz de usuario. Sirven para que un CRUD-L responda desde el primer momento, pero deben sustituirse.

## Vista de lista

Para definir una vista de lista personalizada, cree un archivo `list.dk` en la carpeta de la entidad.

`output` contiene la lista de elementos devuelta por la operación LIST.

```
#include "dkli.dkh"

#$
div(id="view" class="container")
{
    ##
    #include "functions.dkh"
    #include "serialize.dkh"
    ##

    div(id="top_area")
    {
        form(method="GET" action="./" id="filters")
        {
            input(type="text" name="texto" value=$"#<@@(@crud_context,'parameters/texto')>")
            button(type="submit"){"Filtrar"}
        }
        a(href="_new"){"Agregar"}
    }

    div(id="work_area")
    {
        table(class="table")
        {
            thead{ tr{ th{"Código"} th{"Nombre"} } }
            tbody
            {
                ##
                ref filas = @@(@crud_context,"&output")
                for i=0; i<@count(filas)
                {
                    ref f = @item(filas,i)
                    ##
                    tr
                    {
                        td{ a(href=$"#<@@(f,'$sys_pk')>"){ $"#<@@(f,'$codigo')>" } }
                        td{ $"#<@@(f,'$nombre')>" }
                    }
                    ##
                }
                ##
            }
        }
    }
}
```

## Vista de formulario

Para definir una vista de formulario personalizada, cree un archivo `form.dk` en la carpeta de la entidad.

`output` contiene el elemento de datos: el leído en una operación READ, el vacío producido por `@blank` cuando el identificador es `_new`, o los datos de entrada si la operación falló.

La misma vista atiende el alta y la edición. Para distinguirlas, examine el identificador:

```
##
ref d = @@(@crud_context,"&output")
es_nuevo = @@(@crud_context,"$parameters/_entity_id") == "_new"
##
```

El formulario debe enviarse por POST al mismo punto final:

* Para agregar: `POST` a `.../{tipo}/_new`
* Para editar: `POST` a `.../{tipo}/{id}`

En el segundo caso, el controlador reescribe internamente el método como PATCH. Ver [Patrón PRG y redirección](prg-y-redireccion.md).

Recuerde incluir `sys_recver` como campo oculto en la edición; sin él, el control de concurrencia no puede funcionar.

```
form(method="POST" action="")
{
    input(type="hidden" name="sys_recver" value=$"#<@@(d,'$sys_recver')>")
    input(type="text"   name="codigo"     value=$"#<@@(d,'$codigo')>")
    input(type="text"   name="nombre"     value=$"#<@@(d,'$nombre')>")
    button(type="submit"){"Guardar"}
}
```

## Vista de error

`error.dk` se emplea cuando falla una operación LIST, READ o DELETE en modo HTML. Los fallos de CREATE y UPDATE se dirigen a la vista de formulario, para permitir la corrección.

`error/message` contiene el texto del error.

## Plantilla envolvente

Si existe una plantilla configurada y la operación produjo un error, la salida de la vista se coloca en `response/text` y se envuelve en esa plantilla.

La plantilla se determina así:

1. El valor `httm_template` de la entidad en el archivo `.crudl`
2. El valor `httm_template` global del archivo `.crudl`
3. `{@base_path}/{@path_root}/_protected/default.htt`

Puede establecer directamente `@path_template_protected` para fijar la ruta.

Ver [Plantillas e inclusiones automáticas](../website.md).

## Inyección de scripts

El miembro `script` del contexto contiene el texto de un archivo asociado a la vista, declarado en el archivo `.crudl` bajo `entities/{entidad}/scripts/{nombre_de_vista}`.

Permite mantener el JavaScript de una vista en un archivo aparte y sustituirlo por host sin tocar la vista:

```
script{ $"#<@@(@crud_context,'$script')>" }
```

## Vistas con Websencia

Una vista puede definirse como una página en formato JSON de Websencia en lugar de un programa DKL.

Para ello, indique el nombre de la vista con la notación `archivo$vista`:

```
@form_view = "facturacion$captura_factura"
@list_view = "facturacion$consulta_facturas"
```

Donde:

* `facturacion` es el nombre del archivo de Websencia. Si no lleva extensión, se le agrega la indicada en `@websencia_ext` (predeterminado `.jsnwm`).
* `captura_factura` es el nombre de la vista dentro de ese archivo.

El archivo se busca en la carpeta de la entidad y después en la ruta común de entidades, siguiendo la misma cadena de resolución que las vistas DKL.

El renderizado lo realiza `websencia.dk`, que carga los modelos de vista del archivo, selecciona la indicada y la renderiza recibiendo el mismo `@crud_context` que recibiría una vista DKL.

## Vista genérica JSON

Cuando `Accept` no incluye `text/html`, no se emplean vistas específicas. La salida la produce `view.dk`:

* En caso de éxito, se serializa `output` como JSON.
* En caso de error, se serializa el objeto de error.

En ambos casos el tipo de contenido es `application/json;charset=utf-8`.

Los modelos específicos sí se emplean en este modo. Ver [Web services REST](rest.md).

## Sustitución del controlador de vistas

El puntero `@view_controller` recibe la operación y los parámetros, y es responsable de establecer `@success_view` y `@fail_view`. Puede sustituirlo desde el `controller.dk` de una entidad para alterar por completo la correspondencia entre operaciones y vistas.

Los punteros `@success_view` y `@fail_view` también pueden establecerse directamente. Ambos reciben `&params, &input_data, &output_data, &error_info`.

## Documentos relacionados

* [Patrón PRG y redirección](prg-y-redireccion.md)
* [Archivo de configuración `.crudl`](configuracion.md)
* [Ayudantes para generar HTML](../uielements.md)
* [Plantillas e inclusiones automáticas](../website.md)
