Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Custom Blocks

335 views
Skip to first unread message

Max Stephen Russell

unread,
Feb 6, 2025, 12:57:48 PMFeb 6
to Blockly
I'm looking at the "Add Custom Blocks" guide.

https://developers.google.com/blockly/guides/configure/web/custom-blocks

Seems to me the guides are contradictory, but is this in fact the recommended way (aside from special considerations such as mutators) to define a block?:

Blockly.Blocks['string_length'] = { init: function() { this.jsonInit({ "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" }); } };

And for the toolbox reference, isn't XML considered the outdated format?

Thank you sincerely.

-Steve

Max Stephen Russell

unread,
Feb 6, 2025, 1:47:38 PMFeb 6
to Blockly
Sorry, looks like the formatting of the snippet went awry. Let me try again....

Blockly.Blocks['string_length'] = {
  init: function() {
    this.jsonInit({
      "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"
    });
  }
};

Ronald Bourret

unread,
Feb 6, 2025, 2:51:48 PMFeb 6
to blo...@googlegroups.com
There are multiple ways to create blocks and the documentation isn't consistent about which one it uses. The only real recommendation is that you define blocks with JSON instead of JavaScript whenever possible. It is both easier to understand and more flexible when it comes to localization. Note that some functionality is available only in JavaScript, but you can mix the two.

It helps to understand what is actually happening.

Block definitions are objects that include an init property. The value of this property is a function that initializes a block. For an example of this function, see the JavaScript tab on the page you linked to. A block definition object can include other properties, such as destroy, but these are less common. Block definitions are stored in Blockly.Blocks, which is an object indexed by the block name.

When you pass a JSON definition to Blockly, Blockly converts it to JavaScript function calls. Depending on how you pass the JSON to Blockly, Blockly may wrap these in an init function, construct a block definition object, and store that block definition in Blockly.Blocks.

You can add definitions to Blockly.Blocks in multiple ways:
  • Create a block definition object by hand and directly add it to Blockly.Blocks. This is what is shown on the page you linked to. Note the use here of jsonInit, which accepts a JSON definition and turns it into function calls. Using jsonInit is common when you want to mix JSON and JavaScript function calls. For an example of this, see the second example in JSON format versus JavaScript API. You can also mix JSON and JavaScript with extensions.
  • defineBlocksWithJsonArray takes an array of JSON definitions, creates init functions and definition objects from these definitions, and adds the definitions directly to Blockly.Blocks. See the first example in JSON format versus JavaScript API.
  • createBlockDefinitionsFromJsonArray takes an array of JSON definitions and returns an array of definition objects. You can these merge this array with Blockly.Blocks or pass it to defineBlocks, which adds the definitions to Blockly.Blocks. See Block definitions.
One final point of possible confusion: The "JSON" objects we refer to are just plain JavaScript objects.

Ronald Bourret
Technical Writer (Provided by Synergis)


--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/blockly/a1edea5f-95e4-42b3-8776-21159b3ce813n%40googlegroups.com.

Mark Friedman

unread,
Feb 6, 2025, 2:59:40 PMFeb 6
to blo...@googlegroups.com
Thank you Ronald!  That issue is one of my longstanding nits about the Blockly documentation. Though, to be fair, lots of documentation out there on the internet conflates JSON with plain JavaScript objects, so Blockly is not alone ;-)

-Mark


On Thu, Feb 6, 2025 at 11:51 AM 'Ronald Bourret' via Blockly <blo...@googlegroups.com> wrote:
... 

Max Stephen Russell

unread,
Feb 6, 2025, 3:39:34 PMFeb 6
to Blockly
Ronald,

Thank you for another pot of gold! I will be referring to it, and responding to it, as I begin building my inventory of custom blocks.

-Steve

Max Stephen Russell

unread,
Feb 7, 2025, 3:19:22 PMFeb 7
to Blockly
Does custom code generation sometimes, or always, imply a custom block?

Ronald Bourret

unread,
Feb 7, 2025, 3:28:58 PMFeb 7
to blo...@googlegroups.com
Always.

While it's technically feasible to overwrite the code generator for a built-in block, this would violate the rule against monkeypatching Blockly. There also doesn't seem to be much point to it -- if you have an if/then/else block, I can't think of a reason to generate different code from what it already generates.

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Feb 7, 2025, 3:36:41 PMFeb 7
to Blockly

Thanks, Ronald. I don’t touch patches or workarounds, etc. I am only looking forward, which also means I will only build on Blockly’s features.

-Steve

Max Stephen Russell

unread,
Feb 7, 2025, 4:23:59 PMFeb 7
to Blockly
(Ron says:)
  • defineBlocksWithJsonArray takes an array of JSON definitions, creates init functions and definition objects from these definitions, and adds the definitions directly to Blockly.Blocks. See the first example in JSON format versus JavaScript API (end quote)
——————————

defineBlocksWithJsonArray 
strikes me as a very clean and readable design but apparently gives me no access to lower level functionality, while the JavsScript equivalent exposes the lower level functionality and enables extensions — in short, maximum acceptable possibilities. If I have ten, or if I have a hundred, such custom blocks, would I compose that number of variations of:

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');
  }
};

in a file such as custom-blocks.js?
On Thursday, February 6, 2025 at 2:51:48 PM UTC-5 rbou...@google.com wrote:

Alan Smith

unread,
Feb 7, 2025, 5:24:31 PMFeb 7
to blo...@googlegroups.com
I don't know if this applies to you, but we have areas where we have one custom block and we configure it with extraState in the JSON for ones that are very close to each other so we don't have to have hundreds of custom blocks in Javascript.

Hope this helps,

alan


Max Stephen Russell

unread,
Feb 7, 2025, 5:28:47 PMFeb 7
to Blockly
I will look again at that. Thanks very much, Alan.

-Steve

Ronald Bourret

unread,
Feb 7, 2025, 5:35:35 PMFeb 7
to blo...@googlegroups.com
While JavaScript does provide more functionality than JSON, you can mix the two using extensions. Write the JavaScript functionality in one or more extensions (functions) and then reference these extensions using the extensions keyword in JSON.

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Feb 7, 2025, 5:51:00 PMFeb 7
to Blockly
Ronald,

Does this mean I would begin with the JSON definition, such as defineBlocksWithJsonArray? Then create the extension in JS? Then assign the extension to the JSON extensions property? I know I may sound like a kindergartner, but I am trying to make my first effort to launch a block as clear as possible. I will be in outer space soon enough. 😎

Thanks.

Steve

Ronald Bourret

unread,
Feb 7, 2025, 5:59:35 PMFeb 7
to blo...@googlegroups.com
Yes. For example:

Blockly.Extensions.register(
   'my_tooltip_extension',
   function() {
     // Obviously, you will do something more interesting...
     this.setTooltip('my tooltip');
   });

defineBlocksWithJsonArray([
  {
    type: "custom_block",
    ...
    extensions: ['my_tooltip_extension'],
  }]);

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Feb 7, 2025, 6:06:41 PMFeb 7
to Blockly
Oh, so that’s two steps instead of my three. The extension is written totally independently of any block, is assigned in the block definition which of course does not need to be called a second time for its extension property, and furthermore the extension may be assigned to any number of blocks.

Am I on the right track?

Steve

Ronald Bourret

unread,
Feb 7, 2025, 6:40:28 PMFeb 7
to blo...@googlegroups.com
Yes

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Feb 7, 2025, 7:03:23 PMFeb 7
to Blockly
Wonderful. Thank you, Ron. 

Steve

Max Stephen Russell

unread,
Feb 8, 2025, 11:48:27 AMFeb 8
to Blockly
I don’t see it explicitly stated, so I’ll ask: Can an extension be assigned to a built-in block?

-Steve

Max Stephen Russell

unread,
Feb 8, 2025, 1:43:02 PMFeb 8
to Blockly
I'll have to correct myself at least in regard to mutators, which in the guide are explicitly discussed in relation to built-in blocks.

Ronald Bourret

unread,
Feb 11, 2025, 12:37:30 PMFeb 11
to blo...@googlegroups.com
In regards to your question about assigning an extension to a built-in block, the answer is yes and no.

Built-in blocks can certainly use extensions, but I don't think that is what you're asking. Instead, I'm guessing that you would like to add an extension to a built-in block. While this is possible, it's not recommended. Instead, you should copy the code for the built-in block and use it to create a modified block. For more information, see Modifying or Patching existing blocks with javascript.

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Feb 11, 2025, 1:00:07 PMFeb 11
to Blockly
Thank you for replying, Ron. Actually, I was just trying to understand which blocks are legitimate candidates for extension assignments, to build my comprehension of Blockly. The discussion you pointed me to is another example of the kind of customization I strictly avoid and never expose my users to.

Steve

Max Stephen Russell

unread,
Feb 11, 2025, 2:34:29 PMFeb 11
to Blockly
In my first experiment to only emulate the custom block and methodology provided in the starter app, I am stuck on forBlock. My own custom block complains (1) if I concatenate anything for code generation beyond ${fieldValue}: (SyntaxError: Unexpected string) and  (2) when I attach a block to its bottom connection:  (SyntaxError: Unexpected identifier 'addText'').

I would appreciate being bailed out again as I close in on success!

Thank you. -Steve

/**
 * @license
 * Copyright 2023 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import {javascriptGenerator, Order} from 'blockly/javascript';

// Export all the code generators for our custom blocks,
// but don't register them with Blockly yet.
// This file has no side effects!
export const forBlock = Object.create(null);

forBlock['add_text'] = function (block, generator) {
  const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''";
  const addText = generator.provideFunction_(
    'addText',
    `function ${generator.FUNCTION_NAME_PLACEHOLDER_}(text) {

  // Add text to the output area.
  const outputDiv = document.getElementById('output');
  const textEl = document.createElement('p');
  textEl.innerText = text;
  outputDiv.appendChild(textEl);
}`,
  );
  // Generate the function call for this block.
  const code = `${addText}(${text});\n`;
  return code;
};

forBlock['setup01'] = function (block, generator) {
    // Collect argument strings
    const fieldValue = generator.quote_(block.getFieldValue('COUNTRY')); // dropdown
    const fieldValue2 = generator.quote_(block.getFieldValue('labelBillOrLaw')); // serializable label
    const fieldValue3 = generator.quote_(block.getFieldValue('labelLawNum')); // label

    // Trying to generate some code while troubleshooting
  const code = `${fieldValue}`; // ${fieldValue2} ${fieldValue3}`;
  return code;
};


Max Stephen Russell

unread,
Feb 12, 2025, 7:14:55 AMFeb 12
to Blockly
Okay, in the generated code output I now I see that I was not finishing the line of generated code with a semicolon. Therefore all subsequent output was piling on to an open line of generated code.

Steve

Message has been deleted

Max Stephen Russell

unread,
Feb 13, 2025, 10:47:08 AMFeb 13
to Blockly
Let's say I want a custom block that displays "IF" and "ELSE". Then I read the following at https://developers.google.com/blockly/guides/create-custom-blocks/overview:

"Blockly comes with a large number of pre-defined blocks.... In most cases the easiest approach is to just find a really similar block which already exists, copy it, and modify it as needed."

Is that a suggestion to go to a Blockly definitions file such as https://github.com/google/blockly/blob/develop/blocks/logic.ts and feel free to use the following for my starting point?:

  // If/else block that does not use a mutator.
  {
    'type': 'controls_ifelse',
    'message0': '%{BKY_CONTROLS_IF_MSG_IF} %1',
    'args0': [
      {
        'type': 'input_value',
        'name': 'IF0',
        'check': 'Boolean',
      },
    ],
    'message1': '%{BKY_CONTROLS_IF_MSG_THEN} %1',
    'args1': [
      {
        'type': 'input_statement',
        'name': 'DO0',
      },
    ],
    'message2': '%{BKY_CONTROLS_IF_MSG_ELSE} %1',
    'args2': [
      {
        'type': 'input_statement',
        'name': 'ELSE',
      },
    ],
    'previousStatement': null,
    'nextStatement': null,
    'style': 'logic_blocks',
    'tooltip': '%{BKYCONTROLS_IF_TOOLTIP_2}',
    'helpUrl': '%{BKY_CONTROLS_IF_HELPURL}',
    'suppressPrefixSuffix': true,
    'extensions': ['controls_if_tooltip'],
  },

Thank you.

Steve

Ronald Bourret

unread,
Feb 13, 2025, 10:57:02 AMFeb 13
to blo...@googlegroups.com
Yes.

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Feb 13, 2025, 11:05:19 AMFeb 13
to Blockly
Thanks very much, Ron!

-Steve

Neil Fraser

unread,
Feb 13, 2025, 11:23:24 AMFeb 13
to blo...@googlegroups.com
Sadly there isn't an accepted term for this.  This data structure can't contain a NaN or Infinity value.  It can't contain an object that inherits from anything.  It can't contain circular references.  It can't contain an undefined, or a sparse array.  These are restrictions specifically imposed by the JSON standard.

I suppose one could call it "deserialized JSON".  Though if memory serves, using the 's-word' even more extensively might not go down well.

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.


--
Neil Fraser, Switzerland
https://neil.fraser.name
Message has been deleted

Ronald Bourret

unread,
Feb 13, 2025, 12:11:35 PMFeb 13
to blo...@googlegroups.com
Does it matter if the object is JSON-able? I haven't looked at the serialization code, but jsonInit looks for specific properties, so as long as those are legal, it shouldn't matter if the object contains other properties that aren't legal in JSON.

Ronald Bourret
Technical Writer (Provided by Synergis)

Neil Fraser

unread,
Feb 13, 2025, 12:14:59 PMFeb 13
to blo...@googlegroups.com
In practice it usually doesn't need to be serializable.  But the original intent of the JSON format for blocks was so that the block definitions could be shared between the web, iOS, and Android ports of Blockly.

Mark Friedman

unread,
Feb 13, 2025, 2:02:35 PMFeb 13
to blo...@googlegroups.com
I think that there are (at least) three different uses of the term JSON that get used in the Blockly context:
  • JSON formatted strings.  I think that these could be referred to as "JSON" or"JSON strings"
  • Simple non-class JavaScript objects.  These could be referred to as "POJOs" (plain old JavaScript objects) or perhaps just "JavaScript objects".
  • JavaScript objects that are intended to be serialized at some point into JSON formatted strings.  This is what I think we don't have a good term for. Perhaps "serializable JavaScript objects"?  Though that doesn't quite trip off the tongue.
-Mark


Max Stephen Russell

unread,
Feb 13, 2025, 8:44:05 PMFeb 13
to Blockly
Now that I have modified the message labels of the controls_if block definition, the next step seems to be that I need to bring its complex php generating code file ‘logic.ts” into my project, so that I can use it with minimal labor and customization.. Am I correct?

Thanks for the continued assistance as I’m climbing into the saddle.

Steve

Max Stephen Russell

unread,
Feb 15, 2025, 5:24:26 PMFeb 15
to Blockly
Looks like I have crossed this bridge by creating a block of 'type': 'controls_if' and changing the messages to IF and THEN. The code output is generated without any further preparations by me. All seems well and miraculous, though I don't feel that I ever saw this explained in the guides. Mutators are explicitly discussed in relation to built-in fields, but not extensions in general, so I thought I would try, thinking surely there has to be inheritance of some sort when it comes to generating code for custom blocks.

Steve

Max Stephen Russell

unread,
Feb 15, 2025, 5:51:50 PMFeb 15
to Blockly
I should have said I did make an extension to change the message text from “if-do” but ended up not assigning it and, as far as I can tell, not needing it.

Max Stephen Russell

unread,
Feb 17, 2025, 8:42:34 AMFeb 17
to Blockly
Can 

    {
      kind: 'category',
      name: 'Variables',
      categorystyle: 'variable_category',
      custom: 'VARIABLE',
    },

be replaced with a custom category? I need to remove the math_change block. Before I go any further, I thought I should stop and ask if this can in fact be done and still maintain all the functionality of custom: 'VARIABLE.

-Steve

Max Stephen Russell

unread,
Feb 17, 2025, 12:22:39 PMFeb 17
to Blockly
I see that Christopher has answered this question elsewhere - https://groups.google.com/g/blockly/c/LDsOS5o31Hk/m/0Nad_CqJAAAJ . So I will pick up the discussion over there.

Mark Friedman

unread,
Feb 17, 2025, 2:14:15 PMFeb 17
to blo...@googlegroups.com
Hi Steve,

  Do you want to use the 'math_change' block at all?  If not, then the easiest way to do what you want is to just delete the 'math_change' block from the Blocks object. I.e., something like:

  delete Blockly.Blocks['math_change'];

  If, for some reason, you do want to still include the 'math_change' block in your app, but not want it in the Variables category, things get significantly more complicated, but potentially doable.

-Mark


Max Stephen Russell

unread,
Feb 17, 2025, 2:41:47 PMFeb 17
to Blockly
Mark - Wow. This is a tremendous help and relief. Thank you! -Steve

Max Stephen Russell

unread,
Feb 18, 2025, 9:38:21 PMFeb 18
to Blockly
Is there a way to change a controls_if block's messages from "if -- do" to "IF -- THEN" without having to create a custom block? If not, can I safely copy the built-in code generation to the custom forBlock(), or is that even a good idea? I have an IF-THEN block that I thought was a winner, until the javascript generator got a hold of it.

Steve

Max Stephen Russell

unread,
Feb 18, 2025, 11:35:19 PMFeb 18
to Blockly
Well, I copied the javascript controls_if generator code into my custom IF-THEN block's forBlock(), simply changed DO to THEN, and now code is being generated perfectly, as far as I can tell. But if Blockly was to update its generator, my custom implementation would remain the same, so is this the proper (or best) solution or not? I know I like it!

Thanks. This is a very important point of learning for me as I have been trying to understand my options against the backdrop of inheritance I used to enjoy in C++.

Steve

Ronald Bourret

unread,
Feb 19, 2025, 12:07:02 PMFeb 19
to blo...@googlegroups.com
Copying and modifying definitions and generators is the best solution. Blockly does not support inheritance of definitions/generators.

You are correct that your code would not reflect any changes Blockly makes in the future, but changes to built-in blocks are fairly rare.

Ronald Bourret
Technical Writer (Provided by Synergis)

Max Stephen Russell

unread,
Feb 19, 2025, 12:26:49 PMFeb 19
to Blockly
Thank you BIG time, Ronald, for a full and educational response to my concerns.  -Steve

Max Stephen Russell

unread,
Feb 22, 2025, 2:12:45 PMFeb 22
to Blockly
Can anyone tell me where I'm going wrong here? Thank you. -Steve

Uncaught runtime errors:
ERROR
Unexpected string
SyntaxError: Unexpected string
    at runCode (webpack-internal:///./src/index.js:77:8)
    at eval (webpack-internal:///./src/index.js:109:3)
    at WorkspaceSvg$$module$build$src$core$workspace_svg.fireChangeListener (webpack-internal:///./node_modules/blockly/blockly_compressed.js:1158:456)
    at fireNow$$module$build$src$core$events$utils (webpack-internal:///./node_modules/blockly/blockly_compressed.js:75:340)

------
Here's my block:

      type: 'setup01',
      message0: '%1 %2 %3 %4',
      args0: [
          {
              type: 'field_dropdown',
              name: 'COUNTRY',
              options: [
                ['United States', 'US'],    // Default (first in list)
                ['Afghanistan', 'AF'],
                ['Albania', 'AL'],
             // shortened for this post
                ['Vanuatu', 'VU'],
                ['Vatican City', 'VA'],
                ['Venezuela', 'VE'],
                ['Vietnam', 'VN'],
                ['Yemen', 'YE'],
                ['Zambia', 'ZM'],
                ['Zimbabwe', 'ZW']
              ]
          },
          {
              type: 'field_label_serializable',
              text: 'Real-Time Bill',
              name: 'labelBillOrLaw'
          },
          {
              type: 'field_label_serializable',
              text: '100-25',
              name: 'labelLawNum'
          },
          {
              type: 'input_dummy',
              name: 'dum001'

          }
      ],
      previousStatement: null,
      nextStatement: null,
      colour: 225,
      tooltip: 'Select a country from the list.',
      helpUrl: ''
    },

------
Here's my generator function:

forBlock['setup01'] = function (block, generator) {
    const fieldValue = generator.quote_(block.getFieldValue('COUNTRY'));
    const fieldValue2 = generator.quote_(block.getFieldValue('labelBillOrLaw'));
    const fieldValue3 = generator.quote_(block.getFieldValue('labelLawNum'));

    const comment = '// Show country code, current status (Bill or Law),\n// and assigned Bill Number.\n';
    const code = `${comment}${fieldValue} ${fieldValue2} ${fieldValue3};\n`;
    return code;
};

------
And here's the complainer:

const runCode = () => {
  const code = javascriptGenerator.workspaceToCode(ws);
  codeDiv.innerText = code;
  outputDiv.innerHTML = '';
  eval(code);
};

-------
The generated code div shows the intended results after closing the error screen:

// Show country code, current status (Bill or Law),
// and assigned Bill Number.
'US' 'Real-Time Bill' '100-25';

Max Stephen Russell

unread,
Feb 22, 2025, 3:37:20 PMFeb 22
to Blockly
Ah - the error disappeared after I set the return variable (code) to a meaningful statement.

-Steve

Max Stephen Russell

unread,
Mar 4, 2025, 5:36:03 PM (12 days ago) Mar 4
to Blockly
Is there a perfectly reliable way to set the width of a custom text block?

On Wednesday, February 19, 2025 at 12:07:02 PM UTC-5 rbou...@google.com wrote:

Max Stephen Russell

unread,
Mar 4, 2025, 7:56:56 PM (12 days ago) Mar 4
to Blockly
This is my block, in case it helps to give me an example of how to widen/lengthen a block:

  {
    type: 'short_bill_description',
    message0: '%1',
    args0: [
      { type: 'field_input', name: 'DESCRIPTION', text: 'What is this bill about? Enter a short description.'}
    ],
    previousStatement: 'header2',
    nextStatement: 'notHeader',
    colour: 160,
    tooltip: 'Enter a short description for this bill.',
    enableContextMenu: true
  },

Thanks very much.

-Steve

Max Stephen Russell

unread,
Mar 4, 2025, 8:15:15 PM (12 days ago) Mar 4
to Blockly
Something tells me I should be asking about lengthening the text input field, not the block itself.

Max Stephen Russell

unread,
Mar 8, 2025, 9:39:03 PM (7 days ago) Mar 8
to Blockly
Reply all
Reply to author
Forward
0 new messages