Skip to main content

Block properties

Block properties store settings and information for a block in the form definition. The builder part of a block informs the builder about the properties that should be stored in the definition. The runner part of a block can then use those properties for rendering (or operating) the block.

🏗️ Properties in the builder part

The block properties that need to be stored in the form definition are defined in the builder part of the block. To include a property in the form definition, it needs to be decorated with the @definition decorator. This assures the property is automatically saved to and retrieved from the form definition.

Basic usage

The following example shows the basic usage of this decorator:

block-builder-part.ts
import { tripetto, definition, NodeBlock } from "@tripetto/builder";

@tripetto({
type: "node",
identifier: "example",
label: "Example",
icon: "data:image/svg+xml;base64,PHN2ZyAvPg=="
})
class ExampleBlock extends NodeBlock {
@definition
exampleProperty?: boolean;
}

Supply type information

You can extend the decorator with detailed type information about the decorated property. This type information is used by the builder to transfer values between blocks that implement the same property. This happens when the user switches to another block type for a certain node. See the @definition for the available options. Here is an example where detailed type information is supplied:

block-builder-part.ts
import { tripetto, definition, NodeBlock } from "@tripetto/builder";

@tripetto({
type: "node",
identifier: "example",
label: "Example",
icon: "data:image/svg+xml;base64,PHN2ZyAvPg=="
})
class ExampleBlock extends NodeBlock {
@definition("boolean", "optional")
exampleProperty?: boolean;
}

🏃 Properties in the runner part

The runner part only consumes (reads) the block properties from the form definition. The block property types can be supplied to the class declaration of the block in the runner part. That makes the properties (with the right types) available in the props field of the block class. The following example shows how to do this:

block-runner-part.ts
import { tripetto, NodeBlock } from "@tripetto/runner";

@tripetto({
type: "node",
identifier: "example"
})
class ExampleBlock extends NodeBlock<{
exampleProperty?: boolean;
}> {
// Block implementation here
}
tip

There is no need to apply the readonly keyword for the block properties. They become readonly automatically in the props object.

💂 Type guarding parts

It is possible to define a shared properties interface that serves as a code contract between the builder and runner part of the block. This guards the use of the properties in both parts of the block. To implement that, you first need to define the interface for the block properties:

block-properties.ts
export interface IExampleBlock {
exampleField1: string;
exampleField2?: number;
}

Builder part

Now you can use this interface in the builder part, to assure that part implements all the properties correctly:

block-builder-part.ts
import { tripetto, definition, NodeBlock } from "@tripetto/builder";
import { IExampleBlock } from "./block-properties";

@tripetto({
type: "node",
identifier: "example",
label: "Example",
icon: "data:image/svg+xml;base64,PHN2ZyAvPg=="
})
class ExampleBlock extends NodeBlock implements IExampleBlock {
@definition("string")
exampleField1 = "";

@definition("number", "optional")
exampleField2?: number;
}

Runner part

In the runner part, we can now simply supply the interface to the block class declaration so the props field derives the block property types from the properties interface:

block-runner-part.ts
import { tripetto, NodeBlock } from "@tripetto/runner";
import { IExampleBlock } from "./block-properties";

@tripetto({
type: "node",
identifier: "example"
})
class ExampleBlock extends NodeBlock<IExampleBlock> {
// Block implementation here
}

Using collections

When your block contains a collection, make sure to omit that property in the builder part. The reason for this is that the collection type on the builder part is different from that on the runner part. For example, consider this interface:

block-properties.ts
export interface IExampleItem {
name: string;
}

export interface IExampleBlock {
items: IExampleItem[];
exampleField1: string;
exampleField2?: number;
}

In the builder part we need to omit the items property because the type in the class is different from the type declared in the interface:

import { _, tripetto, definition, name, Collection, NodeBlock } from "@tripetto/builder";
import { IExampleBlock, IExampleItem } from "./block-properties";

class ExampleItem extends Collection.Item<ExampleBlock> implements IExampleItem {
@definition
@name
name = "";
}

@tripetto({
type: "node",
identifier: "example",
label: "Example",
icon: "data:image/svg+xml;base64,PHN2ZyAvPg=="
})
class ExampleBlock extends NodeBlock implements Omit<IExampleBlock, "items"> {
items = Collection.of<ExampleItem, ExampleBlock>(ExampleItem, this);

@definition("string")
exampleField1 = "";

@definition("number", "optional")
exampleField2?: number;
}