Skip to main content
Version: Next

High Order Bricks

Since DRAW v1.17 the brick API allow for easy creation of higher order bricks, meaning that we can pass bricks as parameter of other bricks.

info

More exactly we can pass anonymous inline brick as parameter, but in the near future we will expand this feature to all bricks.

High Order Action

To explore this concept we will implement a functional map function, which takes an array and function (lambda) defining the modification to do on each element of the array. Then returns the modified array when done.

First we need to define our action signature in DRAW:

But what should be the type of callback? We need a function signature that takes the element as an input and returns the modified element as an output. DRAW has a special brick type for this kind of definition called Function Signature which can only be created in advanced mode:

A function signature defines the inputs and outputs, with names and types, of a function, but does not provide an implementation. Function signatures can be used as types within the inputs or outputs of functions and actions, as well as within properties of visual components. They are analogous to an interface in object-oriented programming.

Now we can select our signature for the type of the callback parameter:

We are now ready to implement our brick, generate the boilerplate and replace its content by this:

export default class Map extends ActionBrick {
update($, [array, callback], [forwardEvent, setModifiedArray]) {
Promise.all( // (4)
array.map(element => {
return $.runner(callback) // (1)
.set('element', element) // (2)
.waitFor('modified element'); // (3)
})
).then(values => {
setModifiedArray(values); // (5)
forwardEvent(); // (6)
});
}
}

Many things are happening here, lets take them in order:

  1. we use the runner() helper to run the callback brick, it returns the runner context
  2. we set the callback parameter element value
  3. we wait for the callback output modified element to have a value (waitFor() returns a Promise)
  4. we wait for the complete array of promises to resolve before going forward
  5. we are done here, we set the output modified array
  6. we trigger the action output control flow

You can now test this brick in DRAW, in an action for example:

info

We use here the Parse JSON brick to quickly create a test array. The callback function is defined with a Multiply brick that takes the element input and multiply it by 2.

By running this test action we have in the console:

Array: 1,2,3
Modified array: 2,4,6

That's it for our Map!

info

This Map brick is already available in DRAW, alongside with other functional bricks: For Each, Filter and Reduce.

High Order Function

Functions work the same as Actions in this case, but allow for continuous processing when the inputs change.

High Order Visual Component

Visual Component can also run other visual components. The concept is the same you just have to use a property of type Visual Component.

Here a simple example of a Repeater that has one property renderer of type Visual Component:

export default class Repeater extends VisualBrick {
setupExecution($) {
return $.observe('Renderer', false);
}

render($, [renderer]) {
const parent = document.createElement('div');
if(renderer) {
const renderer$ = $.runner(renderer).setParentElement(parent);
this.getRepeatedProperties($).forEach(prop => {
renderer$.repeat(prop, $.observe(prop));
});
}
return parent;
}

getRepeatedProperties($) {
return ['Width', 'Height'];
}
}

The only difference is that you need to use the setParentElement() method to tell the rendered element where it should be in the DOM.

info

As for Function Signature you can specify a signature for your visual component using Abstract Visual Component from the Marketplace. It is also only visible in advanced mode.

executeLambda() helper

The usage of lambda (defined in softcoded in draw, using function signatures) within code requires extensive usage of methods from the context, knowing which you should call, in which order, and what you should expect for outputs and how to deal with them.

CORE provides a helper for lambdas using the Action pattern:

  • The first input is a Control Flow
  • The first output is a Control Flow
  • The second output is an Error Flow

You have to provide the other custom inputs/outputs of your lambda in the order they are present in your lambda / function signature, as an array respectively.

The package @olympeio/core propose this helper method, which can be used to facilitate the execution of such lambda. The signature of the function is the following:

executeLambda($: BrickContext, lambda: string, inputsValues: any[], customErrorCode: number): Promise<any[]>

Usage example for lambda whose name is myLambdaName and has 3 inputs, 3 outputs. The lambda name is the name shown in draw as an input/property of your brick/component. This is the name you gave to the lambda input of your coded brick / property of your component, etc:

const [output1, output2, output3] = await executeLambda($, 'myLambdaName', ['my', 'inputs', 'array']);