Skip to main content

Conditions

Conditions are used to determine whether a particular branch should be taken or not. Conditions are implemented in the builder part using the ConditionBlock class from the builder package and in the runner part using the ConditionBlock class from the runner foundation package. Each Branch can contain multiple conditions and depending on the culling mode of the branch one, each, all, or none of the conditions should evaluate to true in order to take the branch.

tip

Read the condition block tutorial for step-by-step instructions on building a condition block.

🎭 Condition context

Conditions always operate for a certain context. There are five types of context available (the context type of a condition is defined in the @tripetto decorator used to register a condition to the builder):

  • *: The block context is the whole form (for example, the Device condition that evaluates the type of device used for the form);
  • cluster: The block context is a certain cluster;
  • branch: The block context is a certain branch;
  • node: The block context is a certain node;
  • typeof NodeBlock or string: The block context is a certain NodeBlock type (for example, a condition that verifies if a certain option of the dropdown block is selected). In order to bind the right context to the condition, it is possible to define condition templates for blocks. That enables blocks to serve a list of possible conditions for a block to the builder. See the condition templates section for more information.

✅ Condition evaluation

The condition evaluation code itself is defined by declaring a condition method within the condition block class of the runner part. This method then needs to be decorated with the @condition decorator. This decorator instructs the runner to execute the condition method whenever it wants to evaluate the condition. There are two modes of operation for the condition evaluation:

Synchronous evaluation

A synchronous condition is very simple: It should return true if the condition satisfies or false if it does not:

import { tripetto, condition, ConditionBlock, Callback } from "tripetto-runner-foundation";

@tripetto({
type: "node",
identifier: "example-condition",
})
class ExampleConditionBlock extends ConditionBlock {
@condition
evaluate(): boolean {
return true;
}
}

Asynchronous evaluation

Asynchronous evaluation allows a condition to perform an asynchronous process to evaluate a condition. For example, a block that uses an external API to evaluate a condition.

info

To indicate asynchronous evaluation, the condition method should implement the callback argument and return it as return value.

import { tripetto, condition, ConditionBlock, Callback } from "tripetto-runner-foundation";

@tripetto({
type: "node",
identifier: "example-condition",
})
class ExampleConditionBlock extends ConditionBlock {
@condition
evaluate({ callback }: { callback: Callback<boolean> }): Callback<boolean> {
// Call an endpoint
fetch("/example-server")
.then((response) => {
if (response.ok) {
// All ok, condition passed!
callback.return(true);
} else {
// Not so good, condition failed!
callback.return(false);
}
});

// Indicate asynchronous evaluation
return callback;
}
}
info

Whenever asynchronous evaluation is active, the isEvaluating field of the runner may be set to true in some cases. Runners can use this to indicate the form respondent that the form is "busy" (for example, by showing a loading spinner in the UI).

📒 Condition templates

The builder part of a block can supply a list of possible conditions for the block to the builder. To do so, we need to add a method to the NodeBlock class and then decorate it with the @conditions decorator. This method will then be invoked by the builder when it wants to know if there are any conditions available for the block. We can use the conditions field of the NodeBlock class to define condition templates. Here's an example:

import { tripetto, conditions, NodeBlock } from "tripetto";

@tripetto({
type: "condition",
context: ExampleBlock,
identifier: "example-condition",
label: "Example condition",
icon
})
export class ExampleCondition extends ConditionBlock {
value: boolean;
}

@tripetto({
type: "node",
identifier: "example",
label: "Example block",
icon
})
export class ExampleBlock extends NodeBlock {
@conditions
onConditions(): void {
this.conditions.template({
condition: ExampleCondition,
label: "Example condition is true",
props: {
value: true
}
});

this.conditions.template({
condition: ExampleCondition,
label: "Example condition is false",
props: {
value: false
}
});
}
}

This code adds two condition templates for a condition named ExampleCondition. The props property allows to set the initial value of fields of the condition (in this case, it sets a value to the value field of ExampleCondition class). The context for the condition is the ExampleBlock instance. This allows the builder user to add the condition through the context menu of the node block, like the menu that is shown in the following example:

Condition template methods

You can use the following methods for defining condition templates:

  • template: Creates a new condition template and adds it to the list of templates;
  • group: Adds a group that can contain condition templates and custom commands (can be used to organize condition templates);
  • custom: Adds a custom command to the list of templates.