awe-106 Acciones y dependencias
En este tutorial las pantallas estáticas empiezan a comportarse como aplicaciones. Las acciones definen qué ocurre cuando los usuarios o los componentes lanzan un trabajo. Las dependencias definen cuándo deben reaccionar los componentes a valores, eventos o resultados del servidor.
Ruta rápida
- Empieza con una cadena de acciones de botón pequeña.
- Decide qué acciones se quedan en el navegador y cuáles llaman al servidor.
- Añade dependencias solo donde la pantalla necesite realmente un comportamiento dinámico.
- Prefiere un orden de acciones predecible a cadenas ingeniosas pero difíciles de depurar.
Antes de empezar
Ya deberías tener:
- el esqueleto de una pantalla de awe-102
- criterios, grids o gráficos de awe-103
- las queries o maintains relacionados de awe-104
Mantén un modelo mental sencillo:
button or event -> action stack -> optional server call -> UI update
component values/events -> dependency evaluation -> component update or action launch
Paso 1: Construir el flujo de acciones
Las acciones pueden ejecutarse en el cliente, en el servidor o como una cadena que mezcla ambos.
Normalmente se lanzan desde:
button-actiondentro de los botonesdependency-actiondentro de las dependencias
Ambas usan el mismo modelo de acciones. La diferencia principal es CUÁNDO se ejecutan.
Acciones del lado cliente
Las acciones de cliente operan sobre el estado del navegador o sobre la pantalla actual.
Restablecer todo:
<button-action type="reset"/>
Recargar un grid:
<button-action type="filter" target="customerGrid"/>
Navegar a otra pantalla:
<button-action type="screen" target="customer-detail"/>
Pedir confirmación:
<message id="deleteConfirm" title="Delete record" message="Do you want to continue?"/>
<button-action type="confirm" target="deleteConfirm"/>
Si el usuario cancela el diálogo de confirmación, se descartan las acciones pendientes restantes de esa pila.
Acciones del lado servidor
Cuando el trabajo debe recuperar datos, guardar cambios o lanzar lógica del backend, usa una acción de servidor.
<button-action type="server" server-action="maintain" target-action="saveCustomer"/>
Si el servidor devuelve datos que deben rellenar un componente, indica también target:
<button-action type="server" server-action="data" target-action="loadCustomerSummary" target="summaryGrid"/>
Patrón habitual:
server-action="data"para recuperar datos, como una queryserver-action="maintain"omaintain-silentpara escrituras
Cadenas de acciones síncronas
Por defecto, las acciones se ejecutan de forma síncrona y en el orden en que están escritas.
<button label="BUTTON_CONFIRM" icon="save" id="ButCnf" help="HELP_CONFIRM_BUTTON">
<button-action type="validate" />
<button-action type="confirm" target="saveConfirm" />
<button-action type="server" server-action="maintain" target-action="saveCustomer" />
<button-action type="server" server-action="maintain-silent" target-action="refreshCustomerCache" />
<button-action type="filter" target="customerGrid" />
</button>
Esta es la forma habitual de un flujo de escritura seguro:
- validar
- confirmar si hace falta
- guardar
- refrescar el componente afectado
Mientras se ejecuta la pila síncrona actual, los botones de la aplicación están deshabilitados para evitar flujos que se solapen.
Al abrir un diálogo se crea una nueva pila actual para ese flujo modal:
<button-action type="dialog" target="customerDialog"/>
Acciones asíncronas
Cuando una llamada al servidor puede ejecutarse en paralelo, usa async="true".
<button-action type="server" server-action="data" target-action="loadDashboardStats" target="statsChart" async="true"/>
Esta acción entra primero en la pila normal, lo que significa que los pasos previos de validación o confirmación todavía pueden cancelarla antes de su ejecución.
Usa async para trabajo realmente paralelo, no como opción por defecto.
Inspeccionar el flujo de acciones
Las herramientas de desarrollo pueden ralentizar el flujo de acciones para que puedas inspeccionar lo que ocurre.
alt + shift + `[number]`
Esto añade un retardo en segundos y muestra las pilas de acciones, de modo que se vea con más claridad la ejecución síncrona y asíncrona.
Paso 2: Añadir dependencias con criterio
Las dependencias dan a la pantalla un comportamiento dinámico basado en valores, atributos o eventos.
Pueden definirse en componentes interactivos como botones, criterios, grids y columnas.
Ejemplos:
<button ...>
<dependency ...>
</dependency>
</button>
<criteria ...>
<dependency ...>
</dependency>
</criteria>
<grid ...>
<dependency ...>
</dependency>
</grid>
La dependencia solo se ejecuta cuando coinciden sus lanzadores.
Tipos de lanzador
Lanzadores de valor
Observan los valores de los componentes.
<dependency ...>
<dependency-element id="criterionA"/>
</dependency>
Se ejecuta cuando criterionA tiene cualquier valor o cambia.
<dependency ...>
<dependency-element id="criterionA" condition="eq" value="value1"/>
</dependency>
Se ejecuta cuando criterionA == value1.
<dependency ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>
Se ejecuta cuando criterionA > 3 y criterionA != criterionB.
Lanzadores de atributo
Observan los atributos de los componentes en lugar del valor principal.
<dependency ...>
<dependency-element id="criterionA" attribute="unit" condition="eq" value="EUR"/>
</dependency>
Para los grids o treegrids, las comprobaciones de atributo suelen necesitar el nombre de una columna:
<dependency ...>
<dependency-element id="gridA" column="status" attribute="selectedRowValue" condition="eq" value="READY"/>
</dependency>
También puedes recuperar valores relativos a la fila para hacer cálculos:
<column name="total" component="text">
<dependency ...>
<dependency-element id="gridA" column="price" attribute="currentRowValue" alias="priceValue"/>
<dependency-element id="gridA" column="quantity" attribute="currentRowValue" alias="quantityValue"/>
</dependency>
</column>
Lanzadores de evento
Reaccionan a los eventos de los componentes.
<column name="total" component="text">
<dependency ...>
<dependency-element id="gridA" column="price" attribute="currentRowValue" alias="priceValue"/>
<dependency-element id="gridA" column="quantity" attribute="currentRowValue" alias="quantityValue"/>
<dependency-element id="calculateButton" event="click"/>
</dependency>
</column>
Ejemplo de recálculo al guardar una fila:
<column name="total" component="text">
<dependency ...>
<dependency-element id="gridA" column="price" attribute="currentRowValue" alias="priceValue"/>
<dependency-element id="gridA" column="quantity" attribute="currentRowValue" alias="quantityValue"/>
<dependency-element id="gridA" event="save-row"/>
</dependency>
</column>
Modificadores de dependencia
Evaluación inicial
Usa initial="true" cuando la dependencia deba evaluarse también durante la carga de la pantalla.
<dependency initial="true" ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>
and frente a or
Las dependencias usan lógica and por defecto. Cámbiala a type="or" cuando baste con que se cumpla cualquiera de los lanzadores.
<dependency type="or" ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>
Evaluación invertida
Usa invert="true" cuando quieras la condición contraria sin reescribir toda la expresión de la dependencia.
<dependency invert="true" ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>
Alias para los valores recuperados
Los alias permiten que los lanzadores aporten valores a fórmulas o a actualizaciones de la dependencia.
<criteria component="numeric" id="total">
<dependency initial="true" source-type="formule" target-type="input" formule="[a] + [b]">
<dependency-element id="amountA" alias="a"/>
<dependency-element id="amountB" alias="b"/>
</dependency>
</criteria>
Paso 3: Decidir qué debe hacer la dependencia
Una vez lanzada, una dependencia puede actualizar el componente actual o lanzar acciones en otro lugar.
Actualizar el componente actual
source-type indica a AWE de dónde obtiene los datos la dependencia.
Patrones habituales:
Copiar desde un lanzador
<criteria component="numeric" id="copiedValue">
<dependency source-type="launcher" target-type="input" target-action="a">
<dependency-element id="criterionA" alias="a"/>
</dependency>
</criteria>
Usar un valor estático
<criteria component="text" id="staticValue">
<dependency source-type="value" target-type="input" value="Pending review">
<dependency-element id="criterionA" alias="a"/>
</dependency>
</criteria>
Usar una fórmula
<criteria label="a" id="a" component="numeric" style="col-xs-6 col-sm-3 col-lg-2" value="3"/>
<criteria label="b" id="b" component="numeric" style="col-xs-6 col-sm-3 col-lg-2" value="6"/>
<criteria label="a + b" id="aplusb" component="numeric" style="col-xs-6 col-sm-3 col-lg-2">
<dependency source-type="formule" target-type="input" formule="[a] + [b]">
<dependency-element id="a"/>
<dependency-element id="b"/>
</dependency>
</criteria>
Usar una query
<grid id="grid">
<column name="customerName"/>
<column name="customerStatus"/>
<dependency source-type="query" target-type="input" server-action="data" target-action="loadCustomerDetails">
<dependency-element id="criterionA" alias="a"/>
<dependency-element id="criterionB" condition="ne" value="archived"/>
</dependency>
</grid>
target-type decide después qué parte del componente se actualiza: el valor de entrada, la etiqueta, la unidad, la visibilidad, el estado habilitado, etc.
Ejemplo: habilitar un botón solo cuando un criterio tiene un valor concreto.
<button id="saveButton">
<dependency target-type="enable">
<dependency-element id="criterionA" condition="eq" value="READY"/>
</dependency>
</button>
Ejemplo: mostrar un criterio solo cuando otro criterio tiene un valor concreto.
<criteria id="criterionB" component="text">
<dependency target-type="show">
<dependency-element id="criterionA" condition="eq" value="show"/>
</dependency>
</criteria>
Lanzar acciones desde una dependencia
Algunas dependencias no actualizan su propio componente. Solo lanzan acciones cuando se cumplen las condiciones.
<dependency>
<dependency-element id="gridA" attribute="selectedRows" condition="eq" value="1" />
<dependency-element id="gridA" event="select-row" />
<dependency-action type="server" server-action="data" target-action="loadDetailGrid" target="gridB"/>
</dependency>
Es un patrón maestro-detalle habitual: al seleccionar una fila en un grid se refresca otro grid.
Orden de ejecución y rendimiento
Las dependencias se evalúan en el orden en que están definidas. Si varias dependencias del mismo componente coinciden, se ejecutan de forma síncrona en ese orden.
<criteria id="criterionA" component="hidden">
<dependency source-type="launcher" target-type="input" target-action="value">
<dependency-element id="criterionB" alias="value"/>
<dependency-element id="buttonA" event="click"/>
</dependency>
<dependency source-type="launcher" target-type="input" target-action="value">
<dependency-element id="criterionC" alias="value"/>
<dependency-element id="buttonA" event="click"/>
</dependency>
</criteria>
En ese ejemplo, la segunda dependencia puede sobrescribir a la primera porque se ejecuta después.
Regla de rendimiento: las dependencias son potentes, pero demasiadas hacen que una pantalla sea más difícil de razonar y más lenta en el navegador. Construye el comportamiento dinámico más pequeño que resuelva el problema del usuario.
Qué hacer a continuación
- Vuelve a awe-104 si los descriptores del lado servidor que hay detrás de estas acciones todavía no te quedan claros.
- Continúa con awe-107 cuando una acción de servidor deba ejecutar lógica Java.
- Ten a mano la documentación de la API para consultar todas las opciones de acciones y dependencias cuando el flujo básico de la pantalla ya funcione.