Connection checks restrict which connections (and therefore blocks) can connect to each other.
Connection checks are useful for modeling types. For example, the following three blocks have no business being connected, because they represent code that returns different types:
Connection checks can be used to prevent these blocks from connecting. This gives users instantaneous feedback and prevents many simple mistakes.
How they work
Every connection can be associated with a "connection check" which is a nullable array of strings.
Two connections can connect if:
- They are compatible types (e.g. an output connecting to an input).
- They have at least one string in their connection check in common.
For example, connections with the following two checks could connect, because
they share the 'apple'
string:
['apple', 'ball', 'cat']
['apple', 'bear', 'caterpillar']
But connections with these two checks couldn't connect, because they don't share any strings:
['apple', 'ball', 'cat']
['ape', 'bear', 'caterpillar']
There is one other special case. If either array is null
, then the two
connections can also connect. This lets you define connections that can connect
to anything.
null
['ape', 'bear', 'caterpillar]
Examples
For a list of examples of how to use connection checks, see the Connection check playbook.
Set checks
By default, all connections have a null
connection-check, meaning they can
connect to anything. Connection checks need to be assigned manually.
How you assign connection checks to connections is different depending on whether you're using JSON block definitions, or JavaScript block definitions.
JSON
For top-level connections, you assign the check directly to the property that
defines the connection. The value you assign can be null
, a string (which
becomes the only entry in the connection check), or an array of strings.
{
'type': 'custom_value_block',
'output': 'a connection check entry',
},
{
'type': 'custom_statement_block',
'nextStatement': null, // null check
'previousStatement': ['four', 'connection', 'check', 'entries']
}
For inputs, you can assign the check to a check
property of the input
definition. If the check
property doesn't exist, the check is considered
null
. The value you assign can be a string, or an array of strings.
{
'type': 'custom_block',
'message0': '%1 %2',
'args0': [
{
'type': 'input_value',
'check': 'a connection check entry' // Accepts custom_value_block
},
{
'type': 'input_statement',
'check': ['two', 'entries'] // Accepts custom_statement_block
}
]
}
JavaScript
For top-level connections, you can pass the check directly to the method that
defines the connection. If you don't pass a value, the check is considered
null
. The value you pass can be a string (which becomes the only entry in the
connection check), or an array of strings.
Blockly.Blocks['custom_value_block'] = {
init: function() {
this.setOutput(true, 'a connection check entry');
}
};
Blockly.Blocks['custom_statement_block'] = {
init: function() {
this.setNextStatement(true); // null check
this.setPreviousStatement(true, ['four', 'connection', 'check', 'entries']);
}
};
For inputs, you can pass the check to the setCheck
method, after you have
defined the input. If the setCheck
method isn't called, the check is
considered null
. The value you pass can be a string, or an array of strings.
Blockly.Blocks['custom_block'] = {
init: function() {
this.appendValueInput('NAME')
.setCheck('a connection check entry'); // Accepts custom_value_block
this.appendStatementInput('NAME')
.setCheck(['two', 'entries']); // Accepts custom_statement_block
}
};
Built-in check strings
The built-in blocks have connection checks with the values 'Array'
,
'Boolean'
, 'Colour'
, 'Number'
, and 'String'
. If you want your blocks to
interoperate with the built-in blocks, you can use these values to make them
compatible.
Limitations
This system is quite robust and can solve many use-cases, but it does have a few limitations.
Restrict the greater context
This system does not, by itself, support restricting the "greater context" in
which a connection is allowed to connect. For example, you cannot say that a
break
block is only allowed to exist inside of a loop
block. The connection
checking system only considers the immediate two connections being connected.
You can support this by using the event system to listen to block move events and check if the block is incorrectly positioned.
Blockly.Blocks['custom_block'] = {
init: function() { }
onchange: function(e) {
if (this.workspace.isDragging()) return;
if (e.type !== Blockly.Events.BlockMove) return;
if (!this.getSurroundLoop()) this.outputConnection.disconnect();
}
loopTypes: new Set(); // Your valid *block types* (not connection checks).
getSurroundLoop: function () {
let block = this.getSurroundParent();
do {
if (loopTypes.has(block.type)) return block;
block = block.getSurroundParent();
} while (block);
return null;
},
}
Generic types
This system does not, by itself, support defining generic types. For example, you cannot create an "Identity" block, that "returns" whatever its input is.
You can somewhat support this by actively changing the connection check on the block's output to match its input. Which you can do using the event system to listen to block move events.
Blockly.Blocks['custom_block'] = {
init: function() { }
onchange: function(e) {
if (e.type !== Blockly.Events.BlockMove) return;
this.setOutput(
true, this.getInputTargetBlock()?.outputConnection.getCheck());
}
}
But if the connected block is also generic, then this doesn't work correctly. There is no good work around for this case.
Connection checkers
If this system doesn't work for your use case, you can also change how the connection checks are compared by creating a custom connection checker.
For example, if you wanted to create a more advanced system that handles some of the limitations of this one, you can create a custom connection checker.