Formulario Multilínea
Un
formulario multilínea (ei\_formulario\_ml
) presenta una grilla de
campos repetidos una cantidad dada de filas.
permitiendo recrear la carga de distintos registros con la misma
estructura. La definición y uso de la grilla de campos es similar al
formulario simple con el agregado
de lógica para manejar un número arbitrario de filas.
Definición
Ancho: Pixeles o porcentaje que ocupa el objeto gráficamente. Recordar incluír las medidas (px, %, etc.).
Líneas: En la definición se puede incluir una cantidad inicial de filas una opción permite modificar esta cantidad de filas agregando nuevas o eliminando existentes. Durante la ejecución la cantidad y contenido de cada filas se especifica durante la configuración del componente.
Agregar/Quitar líneas: Se incluyen dos botones para agregar y quitar líneas. El agregado puede ser en línea utilizando javascript (más dinámico) o navegando al server como un evento más (más seguro para hacer validaciones o para incluír cascadas).
Ordenar líneas: Se incluyen dos controles para que el usuario pueda ordenar las líneas subiendolas o bajandolas. Si se defina una columna, el orden se envía a travez de esta columna sino el orden se refleja en el orden en que se envían estas filas a los eventos. Este mismo formato se puede utilizar cuando se carga al componente en caso que se quiera enviar las líneas ordenadas al cliente.
Numerar filas: A la izquierda de cada fila se incluye una numeración comenzando desde uno, es sólo para uso cosmético.
Análisis de cambios: Se explica más abajo.
EF *Totalizar: Dentro de la definición de un ef se puede marcar la opción Totalizar, esto implica que ante el cambio de un valor, esta columna se totaliza automáticamente (sería un procesamiento por defecto).
Carga
El ml espera en la carga un matriz con la primer dimensión representando
el orden de las filas y la segunda un arreglo asociativo del tipo
'id_ef' => valor
. Cuando no se carga con datos al formulario se
utilizan las filas vacías definidas en el editor.
<?php
function conf__formulario($form)
{
$datos = array(
array('id' => 1, 'importe' => 100),
array('id' => 3, 'importe' => 412),
);
$form->set_datos($datos);
//-- *Para la carga, una forma alternativa al set_datos es retornar los datos (return $datos)
}
?>
Eventos
El rol gráfico del ML toma comportamientos del formulario común (en
definitiva se están editando datos) pero también al cuadro ya que posee
una estructura tabular. Es por esto que generalmente se lo utiliza tanto
para modificar datos como para seleccionar existentes. El único modelo
que se incluye inserta el evento modificación como implícito. Para
agregar un evento como el de selección recuerde ponerlo como A nivel de filas
.
Eventos que manejan datos
Entran en esta categoría aquellos eventos no declarados a nivel de fila
y que manejan datos. Los eventos disparados por el ML varía según el
tipo de Análisis de cambios
seleccionado en la definición del objeto,
por defecto ('Sin análisis') maneja un único evento, allí se envian la
matriz de filas por columnas, por ejemplo:
<?php
function evt__form__modificacion($datos)
{
var_export($datos);
}
?>
array (
0 => array ('fecha' => '2005-07-04'),
1 => array ('fecha' => '2005-07-05')
)
Selecciones
Entran en esta categoría aquellos eventos declarados a nivel de fila. El evento se dispara llevando como parametro una clave que identifica la fila.
<?php
function evt__form__seleccion($fila)
{
var_dump($fila);
}
?>
int(1534)
Filtrado de eventos por fila
La presencia o ausencia de un evento (a nivel de fila y no global) puede
depender del contenido de cada fila. Para controlar el comportamiento de
este tipo de eventos debe existir un método con el nombre
conf_evt__idcomp__nombreevento
. El método recibe como
parámetro el evento y es posible por ejemplo anular el evento en esta
fila
<?php
function conf_evt__form__seleccion(toba_evento_usuario $evt, $fila)
{
if ($this->datos[$fila]['no_seleccionable'] == '1'){
$evt->anular();
}
}
?>
Métodos de Análisis
Muchas veces es necesario poder seguir la traza de las modificaciones en
el cliente, que registro se modificaron, cuales se agregaron, cuales se
borraron. Para esto se configura el Análisis de cambios
, este análisis
puede ser en línea o mediante eventos.
Análisis en línea
Cuando es análisis en línea
se agrega una columna
apex_ei_analisis_fila
indicando el tipo de cambio producido. Así
mismo cada fila posee un identificador propio como clave del arreglo
asociativo. Por ejemplo:
<?php
function evt__form__modificacion($datos)
{
var_export($datos);
}
?>
array (
12300 => array('fecha' => '2005-07-04', apex_ei_analisis_fila => 'M'), //Esta fila fue modificada
12301 => array('fecha' => '2005-07-05', apex_ei_analisis_fila => 'A'), //Esta es una fila nueva
12302 => array(apex_ei_analisis_fila => 'B') //Esta es una fila eliminada
)
Análisis por eventos
Cuando es análisis por eventos
se dispara un evento registro_alta
,
registro_baja
o registro_modificacion
por línea, conteniendo la clave
de la fila y los datos si es que lleva. Por ejemplo el set de datos
anterior originaria los siguientes eventos:
<?php
function evt__ml__registro_alta($id, $registro)
{
echo "Dando de alta el registro $id\n";
}
function evt__ml__registro_baja($id)
{
echo "Dando de baja el registro $id\n";
}
function evt__ml__registro_modificacion($id, $registro)
{
echo "Modificando el registro $id\n";
}
?>
Modificando el registro 12300.
Dando de alta el registro 12301.
Dando de baja el registro 12302.
Agregar y Quitar líneas
Por omisión la función de agregar o quitar líneas se realiza en el cliente utilizando javascript, esto brinda mucho dinamismo a la operación sacrificando la posibilidad de incluir lógica de negocio confiable en el proceso. Cuando la decisión de crear o eliminar filas debe pasar por lógica de negocio o cosas que depeden de la base de datos, debe transferirse la responsabildad al lado servidor, es por eso que tanto agregar como quitar puede ser parametrizado para que hagan un viaje al servidor.
Agregado en el Server: Como casi toda interacción con el cliente, el agregado se dispara en el servidor como un evento pedido_registro_nuevo. El evento se puede:
No escuchar: en este caso se muesra al usuario un nuevo registro vacío
Escuchar y agregar un registro con el método toba_ei_formulario_ml::set_registro_nuevo)
Escuchar pero no agrega un registro: Rechaza la solicitud del usuario de agegar el registro.
Validaciones en PHP
Ver las validaciones del formulario simple.
Extensión JS
Se puede usar el mecanismo de extensión javascript para agregar comportamientos en el cliente. Estas extensiones necesitan ser definidas en una subclase del componente, dentro del método toba_ei::extender_objeto_js).
La extensión js se basa en la del formulario simple, con la diferencia que ahora varias filas de estos
efs están involucradas. Así, para direccionar desde
javascript a un ef particular se hace referenciando a la fila específica
ej: this.ef('id_ef').ir_a_fila(2).metodo()
. El hecho que lo que
era una lista de campos se convierta en un matriz hace que todas las
extensiones se vuelvan un poco más complejas.
Validación general y eventos
Tanto la validación general como escuchar un evento particular es identico a la forma que se explica en el formulario simple.
Validación de efs
Las validaciones se definen en forma similar al formulario simple, sólo que ahora la validación recibe como parámetro la fila sobre la que se aplica la validación. Con esta fila es posible referenciar directamente al campo en cuestión.
<?php
echo "
//Valida que las fechas, si están seleccionadas, sean un día lunes.
{$this->objeto_js}.evt__fecha__validar = function (fila) {
var lunes = 1;
var ef_fecha = this.ef('fecha').ir_a_fila(fila);
var fecha = ef_fecha.fecha();
if (fecha != null && fecha.getDay() != lunes) {
ef_fecha.set_error('Sólo está permitido ingresar días lunes');
return false;
}
return true;
}
";
Procesamiento de efs
Los procesamientos se lanzan cuando un ef cambia su valor en el cliente.
El procesamiento se escucha como un evento más y recibe dos parámetros:
uno que indica si el disparo es_inicial
o no y el otro indica el número
de fila donde se inicio el disparo. El procesamiento inicial es aquel
que se da en la carga de la pantalla, sin intervención explícita del
usuario, a partir de allí los procesamientos dependen exclusivamente de
los cambios introducidos por el usuario. Por ejemplo si se cuenta con
tres campos: importe, descuento y neto, este último debe cargarse en
base a la resta de los primeros dos. En la implementación este cambio se
hace escuchando los procesamientos del importe y del descuento:
<?php
echo "
//Cuando se modifica el valor del importe, recalcula el neto
{$this->objeto_js}.evt__importe__procesar = function(es_inicial, fila) {
{$this->objeto_js}.refrescar_importes(es_inicial, fila);
}
//Cuando se modifica el valor del descuento, recalcula el neto
{$this->objeto_js}.evt__descuento__procesar = function(es_inicial, fila) {
{$this->objeto_js}.refrescar_importes(es_inicial, fila);
}
{$this->objeto_js}.refrescar_importes = function(es_inicial, fila) {
var importe = this.ef('importe').ir_a_fila(fila).get_estado();
var descuento = this.ef('descuento').ir_a_fila(fila).get_estado();
this.ef('neto').ir_a_fila(fila).set_estado(importe *descuento);
}
";
?>
En el formulario multilínea existe un procesamiento opcional que puede ser agregado, es la capacidad de sumarizar una columna. Cuando este totalización esta activada en el ef (utilizando el administrador) en cualquier cambio de valor en esa columna se dispara la sumarización de cada campo. Esto funciona bien sólo para efs numéricos (número, porcentaje, moneda, etc.).
Ejemplos
Existe un ejemplo en el proyecto de Referencia, item /Componentes/Formularios/Multilínea.