Saltar al contenido principal
Version: próxima

awe-106 Actions and dependencies

This tutorial is where static screens start to behave like applications. Actions define what happens when users or components trigger work. Dependencies define when components should react to values, events, or server results.

Quick path

  1. Start with a small button action chain.
  2. Decide which actions stay in the browser and which call the server.
  3. Add dependencies only where the screen truly needs dynamic behavior.
  4. Prefer predictable action order over clever but hard-to-debug chains.

Before you start

You should already have:

  • a screen shell from awe-102
  • criteria, grids, or charts from awe-103
  • the related queries or maintains from awe-104

Keep the mental model simple:

button or event -> action stack -> optional server call -> UI update
component values/events -> dependency evaluation -> component update or action launch

Step 1: Build the action flow

Actions can run in the client, on the server, or as a chain that mixes both.

They are usually launched from:

  • button-action inside buttons
  • dependency-action inside dependencies

Both use the same action model. The main difference is WHEN they run.

Client-side actions

Client actions operate on the browser state or current screen.

Reset everything:

<button-action type="reset"/>

Reload a grid:

<button-action type="filter" target="customerGrid"/>

Navigate to another screen:

<button-action type="screen" target="customer-detail"/>

Ask for confirmation:

<message id="deleteConfirm" title="Delete record" message="Do you want to continue?"/>
<button-action type="confirm" target="deleteConfirm"/>
Reset criteria Grid filter Navigation action Confirmation action

If the user cancels the confirmation dialog, the remaining pending actions in that stack are discarded.

Server-side actions

When the work must retrieve data, persist changes, or trigger backend logic, use a server action.

<button-action type="server" server-action="maintain" target-action="saveCustomer"/>

If the server returns data that should populate a component, set target as well:

<button-action type="server" server-action="data" target-action="loadCustomerSummary" target="summaryGrid"/>
Server action returned actions

Common pattern:

  • server-action="data" for query-like retrieval
  • server-action="maintain" or maintain-silent for writes

Synchronous action chains

By default, actions run synchronously in the order they are written.

<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>

This is the usual shape for safe write flows:

  1. validate
  2. confirm if needed
  3. persist
  4. refresh the affected component
Action stack

While the current synchronous stack is running, application buttons are disabled to prevent overlapping flows.

Disabled buttons

Opening a dialog creates a new current stack for that modal flow:

<button-action type="dialog" target="customerDialog"/>
Modal dialog and new stack

Asynchronous actions

When a server call can run in parallel, use async="true".

<button-action type="server" server-action="data" target-action="loadDashboardStats" target="statsChart" async="true"/>

This still enters the normal stack first, which means earlier validation or confirmation steps can still cancel it before execution.

Asynchronous actions

Use async for genuinely parallel work, not as a default.

Inspecting the action workflow

The developer tooling can slow down the action flow so you can inspect what is happening.

alt + shift + `[number]`

This adds a delay in seconds and displays the action stacks so you can see synchronous and asynchronous execution more clearly.

Step 2: Add dependencies deliberately

Dependencies give the screen dynamic behavior based on values, attributes, or events.

They can be defined on interactive components such as buttons, criteria, grids, and columns.

Examples:

<button ...>
<dependency ...>
</dependency>
</button>
<criteria ...>
<dependency ...>
</dependency>
</criteria>
<grid ...>
<dependency ...>
</dependency>
</grid>

The dependency only runs when its launchers match.

Launcher types

Value launchers

These watch component values.

<dependency ...>
<dependency-element id="criterionA"/>
</dependency>

Runs when criterionA has any value or changes.

<dependency ...>
<dependency-element id="criterionA" condition="eq" value="value1"/>
</dependency>

Runs when criterionA == value1.

<dependency ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>

Runs when criterionA > 3 and criterionA != criterionB.

Attribute launchers

These watch component attributes instead of the main value.

<dependency ...>
<dependency-element id="criterionA" attribute="unit" condition="eq" value="EUR"/>
</dependency>

For grids or treegrids, attribute checks often need a column name:

<dependency ...>
<dependency-element id="gridA" column="status" attribute="selectedRowValue" condition="eq" value="READY"/>
</dependency>
selectedRowValue

You can also recover row-relative values for calculations:

<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>

Event launchers

These react to component events.

<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>
Recalculation with button

Row-save recalculation example:

<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>
Recalculation of a column

Dependency modifiers

Initial evaluation

Use initial="true" when the dependency should also be evaluated during screen load.

<dependency initial="true" ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>

and vs or

Dependencies use and logic by default. Change it to type="or" when any launcher should be enough.

<dependency type="or" ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>

Inverted evaluation

Use invert="true" when you want the reverse condition without rewriting the full dependency expression.

<dependency invert="true" ...>
<dependency-element id="criterionA" condition="gt" value="3"/>
<dependency-element id="criterionA" condition="ne" id2="criterionB"/>
</dependency>

Aliases for retrieved values

Aliases let launchers feed values into formulas or dependency updates.

<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>

Step 3: Decide what the dependency should do

Once a dependency is triggered, it can either update the current component or launch actions elsewhere.

Update the current component

source-type tells AWE where the dependency gets its data from.

Common patterns:

Copy from a launcher

<criteria component="numeric" id="copiedValue">
<dependency source-type="launcher" target-type="input" target-action="a">
<dependency-element id="criterionA" alias="a"/>
</dependency>
</criteria>

Use a static value

<criteria component="text" id="staticValue">
<dependency source-type="value" target-type="input" value="Pending review">
<dependency-element id="criterionA" alias="a"/>
</dependency>
</criteria>

Use a formula

<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>
Formula dependency

Use a 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>
Query dependency

target-type then decides what part of the component gets updated: input value, label, unit, visibility, enabled state, and so on.

Example: enable a button only when a criterion has a specific value.

<button id="saveButton">
<dependency target-type="enable">
<dependency-element id="criterionA" condition="eq" value="READY"/>
</dependency>
</button>

Example: show a criterion only when another criterion has a specific value.

<criteria id="criterionB" component="text">
<dependency target-type="show">
<dependency-element id="criterionA" condition="eq" value="show"/>
</dependency>
</criteria>
Reversible actuation

Launch actions from a dependency

Some dependencies do not update their own component. They just launch actions when conditions match.

<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>

This is a common master-detail pattern: selecting a row in one grid refreshes another grid.

Selection of a row reloads another grid

Execution order and performance

Dependencies are evaluated in the order they are defined. If multiple dependencies on the same component match, they execute synchronously in that order.

<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>

In that example, the second dependency can overwrite the first because it runs later.

Performance rule: dependencies are powerful, but too many of them make a screen harder to reason about and slower in the browser. Build the smallest dynamic behavior that solves the user problem.

What to do next

  • Return to awe-104 if the server-side descriptors behind these actions are still unclear.
  • Continue with awe-107 when a server action must execute Java logic.
  • Keep the API docs nearby for exhaustive action and dependency options once the core screen flow already works.