In this tutorial we will see how to give some dynamism to the screen that we have developed. Thanks to actions and dependencies it is possible to make requests to the server, to modify the content of a component given the values of other components or to change the attributes of a criteria.
Actions are executions that can be done on client (browser) or server. This executions may be launched in an ordered way (synchronous executions) or in parallel (asynchronous executions).
Right now, actions may be launched from buttons (
button-action) and dependencies (
dependency-action). Both kinds of actions work in the same way and have the same attributes, the only difference between them is the time they are launched. The former ones are launched when you click the button and the latter ones are when the conditions of the launchers of the dependency are met.
There are several kinds of actions that may be executed on the client. The attribute
type indicates the action we want to execute. The list of available actions can be found here.
This action will delete the content of all criteria and grids on the screen.
When we want the action to be executed on a specific component, we can use its
name in the
target attribute of the action:
This action will tell a grid to reload with the attributes it has defined.
Actions also allow us to navigate between application screens, in this case the
target attribute says what option we want to access:
This action will indicate the system to change the screen for a new one.
Sometimes we want to show a dialog asking for confirmation with a message that we may define like this:
To open a dialog that uses this message, we will define an action of type
This action will open a dialog with a confirmation message like the following:
If the user clicks Accept, already defined actions will be launched. If clicks Cancel then the action stack to be launched will be emptied.
There are other actions like
validatethat also act on the action stack depending on the result. In case that validation is not successful, this action cancels the rest.
There are some actions that are launched against the server. These actions start with the word
server and they require a series of extra attributes: the type of action that we want to execute in the server (
server-action) and the identifier of that action (
If the action returns data (is of type
value), we can say the action to what component we want to give the received data:
Most of times, actions send to the server return more actions, that are stored in the stack before the pending actions (unless they are asynchronous). These actions returned by the server will be executed before the rest and one of them may even cancel the rest (type
Inside of a button or a dependency of type action you can define as many actions as you need:
By default, all action defined are synchronous (they are executed sequentially given the order of execution), and no action is executed until the previous one ends. There are some actions, like
cancel, that remove the rest of actions from the current action stack.
While the current action stack contains actions in execution, all buttons in the application are disabled (to avoid the overload of actions).
If we open a modal dialog with
dialog action, the current stack will freeze and a new one will be created that will manage dialog's actions and will become the current stack:
When opening a new stack, the enabled/disabled state of buttons will depend on the new stack.
There is an attribute that modifies the way an action works:
This action is stored in the stack of synchronous actions, as with the rest, but when it time to be executed arrives, it is removed from the synchronous stack (and the next one is executed), added to the asynchronous one and executed inmediatly.
This allows several requests to the server or concurrent updates of several componentes to be launched. The fact that they are initially stored in the stack of synchronous actions is because this way we can cancel them if, for instance a validation fails or a confirmation is cancelled by the user.
AWE offers a tool for developer that allows to visualize the execution of the defined actions. For this, it is necessary to open browser's console and write:
This adds a delay in the execution of 500 milliseconds (you may change the amount) and also shows the execution of actions in each of the stacks. To the left you can find the asynchronous stack and to the right the synchronous. The current stack is the one found to the left of the synchronous stack.
Dependencies, along with actions, offer dynanism to the generated screen almost effortlessly. They can act on the component where they are defined or launch one or several actions on any screen component.
Dependencies are defined inside of the interactable components of the application:
On a button:
On a criteria:
On a grid:
By default, the launching of the dependency is done when all conditions are met (
Launchers are the conditions to be met for a dependency to be launched. The syntax goes as follows:
By default, launching condition is
is not empty, that say, that the component has a value. Everytime the value of the component changes, all launchers will be checked and if all of them are valid, dependency will be launched.
Value launchers are those that check the value of a component. If the value meets the defined condition, launcher is valid. For instance:
This dependency will be launched when criterioA has any value or when it changes.
This dependency will be launched when criterioA has value
This dependency will be launched when the value of criterioA is greater than
3and the value of criterioA is different from criterioB's.
In some components we need the value of an attribute to launch the dependencies. Each type of component has a series of attributes that can be checked:
This dependency will be launched when the unit of criterioA is
When working with
treegrids is necessary to define the column we want to access to to recover the value:
This dependency will be launched when the cell on column columnaA and selected row from gridA has the value
R. In editable grids, the selected row is the one being edited in that moment.
Attributes are also useful to retrieve values from cells in a row and perform calculations with them. With
currentRowValue the value of the current row is retrieved (these dependencies will be over columns, because otherwise is imposible to know the value of the current row).
This dependency retrieves the values of cells columnaA and columnaB from the same row where columnaC can be found. On their own, these launchers would not execute the dependency, simply it would be taken into account that those cells had any value. It would be necessary to include a event launcher that would cause a recalculation of the whole grid.
Along with attributes, some components launch events at a given time. These events can be used to launch dependencies.
For instance, if we use the previous example and add anevent launcher, the dependency will be launched and the values of those columns will be retrieved every time that event is fired:
Every time a user clicks on button botonA, whole column columnaC from grid gridA will be recalculated.
We can do it even more dynamic and, every time someone edits and saves a row, we will launch the recalculation:
Every time someone saves a modification on a row, column columnaC from grid gridA will be recalculated.
Sometimes is necessary to perform an initial launch of the dependency to check if it has to be launched (by default, this is not done because it would make the application to perform quite badly). To indicate to a dependency that it should make an initial check of values when loading the screen, it is necessary to set the
initial attribute to "true":
This dependency will check when the screen is loaded the values from criterias criterioA and criterioB and will launch the dependency if conditions are met.
By default, a dependency is launched when all launchers meet the conditions defined. This can be changed when the attribute
type="or" (by default its value is
"and"), that will launch the dependency when any of the launchers meet the condition defined.
This dependency will be launched when the condition of the first launcher is met (
criterioA > 3) or when the conditions of the second is met (
criterioA != criterioB).
Some dependencies (not all of them, only the ones having reversible actions) require another dependency to be included to restore the modified value in case that the conditions are not met. To facilitate this kind of dependencies, there is an attribute (
invert="true") that evaluates in the oposite way the conditions defined in the launchers, so if we define the following dependencies:
if first dependency is not launched, second will be, and viceversa.
Launchers may be used also to recover data from other components that are needed for the execution of the dependency (for instance, to calculate a formula). To do this, we need to give an
alias in the launcher where we will indicate the identifier that will be used to obtain the value retrieved:
This dependency will be launched when components
numeroB have value (and also everytime those values change), and also everytime it does it it will update the value of the criteria
suma with the sum of the values of the components. Also, it will check the value of components
numeroB when loading the screen and will launch the dependency if both have values defined initially (because
initial attribute is set to
Once all conditions of components are met, the dependency is executed. Usually a dependency retrieves one or several data and with that it updates the component where is defined.
source-type attribute defines the origin of data that will be used to modify the component where the dependency is defined.
By default, the
none, meaning that if left undefined, it does not try to recover any data.
The origin of data may be one of the launchers (
source-type="launcher" would retrieve the value of the alias defined in the
a static value (
source-type="value", that would retrieve the value from
value attribute in the dependency)
the execution of a formula (
source-type="formule" defined in
As can be seen, when loading the page the first calculation is not done, because it is not defined as
or by the data recovered by a query done on the server (
source-type="query" that requires also the attributes
target-type indicates the part of the component to be updated with the data calculated or recovered from
source-type. Those parts may be the actual value of the component (
label of a criteria, the
In some cases, is not necessary to recover a data to act on a component, but
knowing if the conditions of the dependency have been met or not. This happens with actions like
This dependency would activate the button only when the criterioA has the value 'lala'
Some actuations are reversible, meaning that, if dependency's conditions are not met, the oposite action is executed, so it is not necessary to include two dependencies (one that acts as we want and another to do the opposite when the inverse conditons are met). This happens with actions like
show, that will launch a
hide action in case conditions are not met, an
enable would launch a
This dependency would show the criterioB only when criterioA has the value 'mostrar'. Otherwise, the criteria is kept hidden.
Until now, all dependencies that we have seen act solely on the component (criteria, grid, column, chart) where it was defined. There are other kind of dependencies, action dependencies, that can be defined in any component but they only launch the actions (
dependency-action) that they have defined inside, they do not act with the component.
These dependencies are defined setting to
action the attribute
source-type, one or more launchers are defined and afterwars actions are added (as
This dependency will launch a data load on gridB when we select one and only one row of gridA.
Dependencies are evaluated in the order they are defined. If there is more than one dependency defined on the same component that matches the conditions to be executed in that moment, they will be executed in a synchronous way (in the order they are defined).
In the previous dependency, the criteria criterioA will take the value from criterioC because they are executed in order, the value from the first dependency will be overwritten (value from criterioB) given that criteria criterioC has any value.
Dependencies affect to the performance of a screen (a lot of calculation is done on the browser). Adding too many dependencies to a window may cause a bad user experience, so it is necessary to design ccorrectly the funcionality we want to offer and look for possible alternatives (like updating criteria and grids massively, or initializing the data when the screen loads).