Block definitions describe how a block looks and behaves, including the text, the colour, the shape, and what other blocks it can connect to.
JSON format versus JavaScript API
Blockly has two ways of defining blocks: JSON objects and JavaScript functions. The JSON format is designed to simplify the localization process when developing for languages with different word orderings. The JSON format is the preferred method of defining blocks.
However, the JSON format is not able to directly define advanced features such as mutators or validators. These must be written in JavaScript, usually as extensions.
Apps using the original JavaScript implementation of Blockly can also write block definitions directly to the lower level Blockly API function calls, shown in the various JavaScript examples below.
JSON
Blockly.defineBlocksWithJsonArray([{
"type": "string_length",
"message0": 'length of %1',
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": "String"
}
],
"output": "Number",
"colour": 160,
"tooltip": "Returns number of letters in the provided text.",
"helpUrl": "http://www.w3schools.com/jsref/jsref_length_string.asp"
}]);
JavaScript
Blockly.Blocks['string_length'] = {
init: function() {
this.appendValueInput('VALUE')
.setCheck('String')
.appendField('length of');
this.setOutput(true, 'Number');
this.setColour(160);
this.setTooltip('Returns number of letters in the provided text.');
this.setHelpUrl('http://www.w3schools.com/jsref/jsref_length_string.asp');
}
};
The init
function creates the block's shape. In the context of this
function the keyword this
is the actual block being created.
Both examples load the same 'string_length' block.
On the web, the JSON format is loaded using the initJson
function.
This also allows mixing the two formats in Blockly webpages. It is
preferred to define your block with JSON wherever possible, and use JavaScript
only for parts of block definitions that JSON does not support.
Below is an example of a block that is predominantly defined using JSON, but is extended using the JavaScript API to feature a dynamic tooltip.
JavaScript
var mathChangeJson = {
"message0": "change %1 by %2",
"args0": [
{"type": "field_variable", "name": "VAR", "variable": "item", "variableTypes": [""]},
{"type": "input_value", "name": "DELTA", "check": "Number"}
],
"previousStatement": null,
"nextStatement": null,
"colour": 230
};
Blockly.Blocks['math_change'] = {
init: function() {
this.jsonInit(mathChangeJson);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
return 'Add a number to variable "%1".'.replace('%1',
thisBlock.getFieldValue('VAR'));
});
}
};
Block colour
The primary colour of a block is defined by the JSON colour
property, the
block.setColour(..)
function,
or by using themes and defining a block
style.
JSON
{
// ...,
"colour": 160,
}
JavaScript
init: function() {
// ...
this.setColour(160);
}
See the block colour guide for more details.
Statement Connections
Users can create sequences of blocks using the nextStatement
and
previousStatement
connectors. In Blockly's standard layout, these connections
are on the top and the bottom, with the blocks stacked vertically.
A block with a previous connector cannot have an output connector, and vice versa. The term statement block refers to a block with no value output. A statement block will usually have both a previous connection and a next connection.
nextStatement
and previousStatement
connections can be
typed,
but this feature is not utilized by standard blocks.
Next Connection
Creates a point at the bottom of the block, so that other statements can be stacked below it. A block with a next connection but no previous connection usually represents an event, and can be configured to render with a hat.
JSON
Untyped:
{
...,
"nextStatement": null,
}
Typed (rare):
{
"nextStatement": "Action",
...
}
JavaScript
Untyped:
this.setNextStatement(true); // false implies no next connector, the default
Typed (rare):
this.setNextStatement(true, 'Action');
Previous Connection
Creates a notch at the top of the block, so that it can be connected as a stack of statements.
Blocks with a previous connection cannot have an output connection.
JSON
Untyped:
{
...,
"previousStatement": null,
}
Typed (rare):
{
"previousStatement": "Action",
...
}
JavaScript
Untyped:
this.setPreviousStatement(true); // false implies no previous connector, the default
Typed (rare):
this.setPreviousStatement(true, 'Action');
Block Output
A block may have a single output, represented as a male jigsaw connector on the leading edge. Outputs connect to value inputs. Blocks with an output are usually called value blocks.
JSON
Untyped:
{
// ...,
"output": null,
}
Typed:
{
// ...,
"output": "Number",
}
JavaScript
Untyped:
init: function() {
// ...
this.setOutput(true);
}
Typed:
init: function() {
// ...
this.setOutput(true, 'Number');
}
Blocks with an output connector cannot also have a previous statement notch.
Block Inputs
A block has one or more inputs, where each input has a sequence of fields and may end in a connection. There are several types of built-in inputs.
- Value input: Connects to an output connection of a
value block. A
math_arithmetic
block (addition, subtraction) is an example of a block with two value inputs. - Statement input: Connects to a previous connection of a statement block. The nested section of a while loop is an example of a statement input.
- Dummy input: Does not have a block connection. Acts like a newline when the block is configured to use external value inputs.
- End-row input: Does not have a block connection, and always acts like a newline.
You can also create a custom input, to support custom rendering.
The JSON format and JavaScript API use slightly different models to describe their inputs.
Inputs and Fields in JSON
JSON defined blocks are structured as a sequence of interpolated
message strings ( message0
, message1
, ...), where each interpolation token
(%1
, %2
, ...) is a field or an input end (thus where the input connector
renders, within the message) in the matching JSON argsN
array. This format is
intended to make internationalization easy.
JSON
{
"message0": "set %1 to %2",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "item",
"variableTypes": [""]
},
{
"type": "input_value",
"name": "VALUE"
}
]
}
The interpolation tokens must match the args0
array completely: no duplicates,
no omissions. Tokens may be present in any order, which allows different
languages to change the layout of the block.
Text on either side of an interpolation token is whitespace-trimmed.
Text using the character %
(e.g. when referring to a percentage) should use
%%
so that it is not interpreted as an interpolation token.
The order of the arguments and the argument types define the shape of the
block. Changing one of these strings can completely change the block's layout.
This is particularly important in languages that have a different word order
than English. Consider a hypothetical language where "set %1 to %2"
(as used
in the example above) needs to be reversed to say "put %2 in %1"
. Changing
this one string (and leaving the rest of the JSON
untouched) results in the following block:
Blockly automatically changed the order of the fields, created a dummy input, and switched from external to internal inputs.
Blockly also automatically replaces any newline character (\n
) in the message
string with an end-row input.
JSON
{
"message0": "set %1\nto %2",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "item",
"variableTypes": [""]
},
{
"type": "input_value",
"name": "VALUE"
}
]
}
Args
Each message string is paired with an args
array of the same number. For
example, message0
goes with args0
. The interpolation tokens
(%1
, %2
, ...) refer to the items of the args
array. Every object has a
type
string. The rest of the parameters vary depending on the type:
You can also define your own custom fields and custom inputs and pass them as args.
Every object may also have an alt
field. In the case that Blockly does not
recognize the object's type
, then the alt
object is used in its place. For
example, if a new field named field_time
is added to Blockly, blocks using
this field could use alt
to define a field_input
fallback for older versions
of Blockly:
JSON
{
"message0": "sound alarm at %1",
"args0": [
{
"type": "field_time",
"name": "TEMPO",
"hour": 9,
"minutes": 0,
"alt":
{
"type": "field_input",
"name": "TEMPOTEXT",
"text": "9:00"
}
}
]
}
An alt
object may have its own alt
object, thus allowing for chaining.
Ultimately, if Blockly cannot create an object in the args0
array (after
attempting any alt
objects) then that object is simply skipped.
A dummy input will be automatically added to the end of the block if the
message
string ends with text or fields that are not contained by an input.
Thus if the last input on a block is a dummy input then it may be omitted from
the args
array and it does not need interpolation into message
. The
automatic addition of a tailing dummy input allows translators to change
message
without needing to modify the rest of the JSON. See the example of
"set %1 to %2"
(no dummy input) and "put %2 in %1"
(dummy input added)
earlier on this page.
implicitAlign0
In rare cases the automatically created trailing dummy input needs to be aligned
to the "RIGHT"
or "CENTRE"
. The default if not specified is "LEFT"
.
In the example below message0
is "send email to %1 subject %2 secure %3"
and Blockly automatically adds a dummy input for the third row. Setting
implicitAlign0
to "RIGHT"
forces this row to be right aligned. This
alignment applies to all inputs that are not explicitly defined in the JSON
block definition, including end-row inputs that replace newline characters
('\n'
) in the message. There is also the deprecated property lastDummyAlign0
that has the same behavior as implicitAlign0
.
When designing blocks for RTL (Arabic and Hebrew), left and right are reversed.
Thus "RIGHT"
would align fields to the left.
message1
, args1
, implicitAlign1
Some blocks are naturally divided into two or more separate parts. Consider this repeat block which has two rows:
If this block were described with a single message, the message0
property
would be "repeat %1 times %2 do %3"
. This string is awkward for a translator,
it is difficult to explain what the %2
substitution means. The %2
dummy
input may also not even be desired in some languages. And there may be multiple
blocks that wish to share the text of the second row. A better approach
is for JSON to use more than one message and args properties:
JSON
{
"type": "controls_repeat_ext",
"message0": "repeat %1 times",
"args0": [
{"type": "input_value", "name": "TIMES", "check": "Number"}
],
"message1": "do %1",
"args1": [
{"type": "input_statement", "name": "DO"}
],
"previousStatement": null,
"nextStatement": null,
"colour": 120
}
Any number of message
, args
, and implicitAlign
properties may be defined
in the JSON format, starting with 0 and incrementing sequentially. Note that
the Block Factory is not capable of splitting messages into multiple parts, but
doing so manually is straightforward.
Inputs and Fields in JavaScript
The JavaScript API includes an append
method for each input type:
JavaScript
this.appendEndRowInput()
.appendField('for each')
.appendField('item')
.appendField(new Blockly.FieldVariable());
this.appendValueInput('LIST')
.setCheck('Array')
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField('in list');
this.appendStatementInput('DO')
.appendField('do');
this.appendDummyInput()
.appendField('end');
Each append method can take an identifier string, used by code generators. Dummy and end-row inputs rarely need referencing and the identifier is usually left unset.
The JavaScript API also includes a generic appendInput
method for appending
custom inputs. Note that in this case, the identifier should
be passed directly to your custom input's constructor.
JavaScript
this.appendInput(new MyCustomInput('INPUT_NAME'))
.appendField('an example label')
All of the appendInput
methods (both generic and non-generic) return the
input object so that they can be further configured using method chaining. There
are three built-in methods used for configuring inputs.
setCheck
JavaScript
input.setCheck('Number');
This optional function is used for type-checking of connected inputs. If given an argument of null, the default, then this input may be connected to any block. See Type Checks for details.
setAlign
JavaScript
input.setAlign(Blockly.inputs.Align.RIGHT);
This optional function is used to align the fields (see below). There are three
self-descriptive values which may be passed as an argument to this function:
Blockly.inputs.Align.LEFT
, Blockly.inputs.Align.RIGHT
, and
Blockly.inputs.Align.CENTER
.
When designing blocks for RTL (Arabic and Hebrew), left and right are reversed.
Thus Blockly.inputs.Align.RIGHT
would align fields to the left.
appendField
Once an input has been created and appended to a block with appendInput
, one
may optionally append any number of fields to the input. These fields
are often used as labels to describe what each input is for.
JavaScript
input.appendField('hello');
The simplest field element is text. Blockly's convention is to use all lowercase text, with the exception of proper names (e.g. Google, SQL).
An input row can contain any number of field elements. Multiple appendField
calls may be chained together to efficiently add several fields to the same
input row.
JavaScript
input.appendField('hello')
.appendField(new Blockly.FieldLabel('Neil', 'person'));
The appendField('hello')
call is actually a shortcut for using an explicit
FieldLabel constructor: appendField(new Blockly.FieldLabel('hello'))
.
The only time one would wish to use the constructor is when specifying a
class name so that the text may be styled using a CSS rule.
Inline vs. External
Block inputs can render as either external or internal.
The block definition can specify an optional boolean controlling whether inputs
are inline or not. If false
then any value inputs will be external (such as
the left block). If true
then any value inputs will be inline (such as the
right block above).
JSON
{
// ...,
"inputsInline": true
}
JavaScript
init: function() {
// ...
this.setInputsInline(true);
}
If not defined then Blockly will use some heuristics to guess which mode is
best. Assuming Blockly makes the right choice, leaving this field undefined
is preferable since different language translations can automatically have
different modes. See the JSON example of "set %1 to %2"
(external inputs) and
"put %2 in %1"
(inline inputs) earlier on this page.
Use inline inputs when a block is likely to have small inputs such as numbers.
The user can toggle this option through the context menu, if the collapse
configuration is enabled (defaults to true if the toolbox has categories).
Fields
Fields define the majority of UI elements within a block. These include the
string labels, images, and inputs for
literal data
such as strings and numbers. The simplest example is the math_number
block,
which uses a field_input
to let the user type a number.
Fields are appended to the block using appendField.
Blockly provides a number of built-in fields, including text inputs, color pickers, and images. You can also create your own fields.
→ More information on built-in fields.
→ More information on custom field creation.
Icons
Icons define UI elements on a block that surface "meta" information about the block.
Icons are appended to the block using addIcon.
Blockly provides a number of built-in icons, including comment icons and warning icons. You can also create your own icons.
→ More information on custom icon creation.
Tooltips
Tooltips offer instant help when the user hovers their mouse over the block. If the text is long, it will wrap automatically.
JSON
{
// ...,
"tooltip": "Tooltip text."
}
JavaScript
init: function() {
this.setTooltip("Tooltip text.");
}
In the JavaScript API, tooltips can also be defined as a function instead of a
static string. This allows for dynamic help. See math_arithmetic
for an
example of a tooltip that changes depending on which dropdown option has been
chosen.
JavaScript
Blockly.Blocks['math_arithmetic'] = {
init: function() {
// ...
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('OP');
var TOOLTIPS = {
'ADD': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD,
'MINUS': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS,
'MULTIPLY': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY,
'DIVIDE': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE,
'POWER': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER
};
return TOOLTIPS[mode];
});
}
};
Using the JavaScript API, blocks can specify a function, instead of a static
string, which returns a the tooltip string. This allows for dynamic tooltips.
See math_arithmetic
for an example.
Customizing
You can also customize the look of your tooltips by providing a custom rendering function. Create a function that accepts two parameters:
- first, a
<div>
element where you will render the content - second, the actual element that is being moused over and that you will show the tooltip for
In the body of the function, you can render whatever content you like into the
div. To get the tooltip string defined on the block being moused over, you can
call Blockly.Tooltip.getTooltipOfObject(element);
where element
is the
second parameter above.
Finally, register this function so Blockly can call it at the appropriate time:
Blockly.Tooltip.setCustomTooltip(yourFnHere);
For an example, see the Custom Tooltips demo.
Help URL
Blocks can have a help page associated with them. This is available to the
users of Blockly for Web by right-clicking the block and selecting "Help"
from the context menu. If this value is null
then the menu will be greyed
out.
JSON
{
// ...,
"helpUrl": "https://en.wikipedia.org/wiki/For_loop"
}
JavaScript
init: function() {
// ...
this.setHelpUrl('https://en.wikipedia.org/wiki/For_loop');
}
Using the JavaScript API, blocks can specify a function, instead of a static string, which returns a URL string, thus allowing for dynamic help.
Change Listeners and Validators
Blocks can have change listener functions that are called on any change to the workspace (including those unrelated to the block). These are primarily used to set the block's warning text, or similar user notification outside the workspace.
The function is added by calling setOnChange with a function and can be done during init or via a JSON extension if you plan to use it on all platforms.
JSON
{
// ...,
"extensions":["warning_on_change"],
}
Blockly.Extensions.register('warning_on_change', function() {
// Example validation upon block change:
this.setOnChange(function(changeEvent) {
if (this.getInput('NUM').connection.targetBlock()) {
this.setWarningText(null);
} else {
this.setWarningText('Must have an input block.');
}
});
});
JavaScript
Blockly.Blocks['block_type'] = {
init: function() {
// Example validation upon block change:
this.setOnChange(function(changeEvent) {
if (this.getInput('NUM').connection.targetBlock()) {
this.setWarningText(null);
} else {
this.setWarningText('Must have an input block.');
}
});
}
}
The system calls the function, passing in the
change event.
Inside the function, this
refers to the block instance.
Because the function is called on any change, if used, developers should ensure the listener runs quickly. One should also be wary of changes to the workspace that might cascade or loop back to the listener.
See the controls_flow_statements
, logic_compare
, and procedures_ifreturn
blocks for examples.
Note that editable fields have their own event listeners for input validation and causing side effects.
Mutator
Mutators allow advanced blocks to change shape, most notably as a result of
users opening a dialog to add, remove, or rearrange components. Mutators may be
added through JSON with the mutator
key.
JSON
{
// ...,
"mutator":"if_else_mutator"
}
Per-block configuration
Block instances have a number of properties that configure how they behave to the user. These can be used to constrain the workspace to reflect certain properties of the domain (e.g., there is exactly one 'start' event), or focus the user's effort (e.g., a tutorial).
Deletable State
block.setDeletable(false);
When set to false, the user will not be able to delete the block. Blocks default to deletable on an editable workspace.
Any block, (even undeletable ones) may be deleted programmatically:
block.dispose();
Editable State
block.setEditable(false);
When set to false, the user will not be able to change the block's fields (e.g. dropdowns and text inputs). Blocks default to editable on an editable workspace.
Movable State
block.setMovable(false);
When set to false, the user will not be able to move the block directly. An immovable block that is a child of another block may not be disconnected from that block, though it will move with its parent if the parent is moved. Blocks default to movable on an editable workspace.
Any block (even immovable ones) may be moved programmatically once it is on a workspace.
block.moveBy(dx, dy)
The starting position for a block on a workspace defaults to (0, 0).
Block data
this.data = '16dcb3a4-bd39-11e4-8dfc-aa07a5b093db';
Data is an optional and arbitrary string that is attached to the block. When the block is serialized the data string is serialized with it. This includes when the block is duplicated or copy/pasted.
Often this is used to associate a block with an external resource.
When serialized to JSON, the data is stored as a top-level property in the block:
{
"type": "my_block",
"data": "16dcb3a4-bd39-11e4-8dfc-aa07a5b093db",
// etc..
}
When serialized to XML (the old iceboxed serialization system) the data string
is stored in a <data></data>
tag within the block:
<block type="my_block">
<data>16dcb3a4-bd39-11e4-8dfc-aa07a5b093db</data>
<!-- etc... -->
</block>
Destruction
Blocks have a destroy
hook, which is called when they are deleted from the
workspace. This can be used to destroy any backing data models/external
resources associated with the block that are no longer needed.
JSON
{
// ...,
"extensions":["destroy"],
}
Blockly.Extensions.registerMixin('destroy', {
destroy: function() {
this.myResource.dispose();
}
});
JavaScript
Blockly.Blocks['block_type'] = {
destroy: function() {
this.myResource.dispose();
}
}
The destroy
method is called after the block's parent has been disposed, but
before any of its children or fields have been disposed.
Context Menus
By default, blocks have a right-click context menu that allows users to do things like add comments, or duplicate blocks.
You can disable the context menu of an individual block by doing:
block.contextMenu = false;
You can also customize the options shown in the menu. To customize the menu for
all blocks, refer to the
context menus documentation.
To customize the menu for an individual block, you can implement
customContextMenu
. This function takes in an array of menu options and
modifies it in place, which means you may both add and remove items.
Each menu option is an object with three properties:
text
is the display text.enabled
is a boolean. When disabled, the option is shown but with grey text.callback
is the function to be called when the option is clicked.