Generate Nested Blocks in the Workspace via JS

346 views
Skip to first unread message

Erin Carvalho

unread,
May 25, 2021, 7:14:47 PM5/25/21
to Blockly
Hi all,
I am working on a debug mode for a portion of a web app that I am building and would like to have a workspace spawn with some blocks on loading the page. Namely, something like this.
Screen Shot 2021-05-25 at 7.10.30 PM.png
I use the code below to generate the orange blocks by default but I can't seem to generate nested blocks the same way. Any tips on how to accomplish this without "saving" the workspace?
let xml = Blockly.Xml.textToDom('<xml><block type="' + type + '"></block></xml>');
Blockly.Xml.appendDomToWorkspace(xml, this.workspace);

Best,
Erin


Jason Schanker

unread,
May 26, 2021, 4:07:23 AM5/26/21
to Blockly
Hi Erin,

You can do this by nesting block/statement/value/next/field elements inside your block ones.  For example, if your go block has a statement input named A, you'd use:

<block type = "go">
  <statement name = "A">
  ... statement block goes here ...
  </statement>
</block>

If the foreach block attached to this statement input had a field named dropdown that started with a field value of water, it would look something like this:

<block type = "go">
  <statement name = "A">
    <block type = "foreach">
      <field name = "dropdown">water</field>
    </block>
  </statement>
</block>

If this foreach block also had a move block connected to its statement input named "DO":

<block type = "go">
  <statement name = "A">
    <block type = "foreach">
      <field name = "dropdown">water</field>
      <statement name = "DO">
        <block type = "move"></block>
      </statement>
    </block>
  </statement>
</block>

If you know the blocks you want to nest in advance, an easy way to get the XML would be to use the Block factory at: https://blockly-demo.appspot.com/static/demos/blockfactory/index.html .  You can import the blocks that you're using, assemble the starter blocks you want to use and then export the resulting workspace, which would then be the string you'd provide to the Blockly.Xml.textToDom function.

You can also create/connect blocks and set field values programmatically instead of importing from XML:

const workspace = Blockly.getMainWorkspace();
const goBlock = workspace.newBlock("go");
const forEachBlock = workspace.newBlock("foreach");
const moveBlock = workspace.newBlock("move");
goBlock.initSvg();
forEachBlock.initSvg();
moveBlock.initSvg();
goBlock.getInput("A").connection.connect(forEachBlock.previousConnection);
forEachBlock.setFieldValue("water", "dropdown");
forEachBlock.getInput("DO").connection.connect(moveBlock.previousConnection);
workspace.render();

It's also possible to create and attach blocks randomly and randomly set field values by using block.inputList to get all of block's inputs and input.fieldRow to get all of input's fields.  So if block referred to a foreach block from the above example, block.inputList[0].fieldRow[1].getOptions() might be used to get the list of options from the dropdown menu from which you could select a random option and block.inputList[1].connection might be the forEachBlock.getInput("DO").connection, which you could then use to connect another block.  You could use information like this to set input value blocks/field values appropriately.  Safety checks to see that the blocks "fit" together would be required as well.

Hope this helps.  I can provide more details about programmatically setting up your workspace if you plan to do this.

Best,
Jason  

feni...@google.com

unread,
May 26, 2021, 7:28:22 PM5/26/21
to Blockly
+1 to Jason's thorough answer. However, constructing XML manually is often a pain--when I want to test, I always construct the block stacks in a workspace, serialize, and then load from XML. Is there a specific blocker to saving the workspace as a way to generate XML?

Cheers,
Rachel

Jason Schanker

unread,
May 27, 2021, 12:05:30 AM5/27/21
to Blockly
Completely agree with Rachel, constructing the block stacks in the workspace is definitely preferable to the tedious manual assembly of XML.  And the recommendation I made to use the Block factory is unnecessary if your toolbox already has all the blocks you want to use for your starter ones.  In that case, you can assemble the blocks in your workspace and then enter Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace())) in the Developer console to serialize.  For quick experimenting with new blocks, you can also enter code for the block definitions (and then to display in the workspace) in the Developer console: 

Blockly.Blocks['go'] = {
  init: function() {
    this.appendDummyInput()
        .appendField("go");
    this.appendStatementInput("A")
        .setCheck(null);
    this.setColour(20);
 this.setTooltip("");
 this.setHelpUrl("");
  }
};

const workspace = Blockly.getMainWorkspace();
workspace.newBlock("go").initSvg(); 
workspace.render();

This is usually what I wind up doing when experimenting.  However, it might be nice to integrate this into the Advanced Blockly Playground.  I opened a new feature request here, accordingly.

Best,
Jason

Jason Schanker

unread,
May 27, 2021, 12:08:10 AM5/27/21
to Blockly
The Advanced Blockly Playground is here.  I accidentally pointed it to my feature request.  Sorry for the additional post.
Reply all
Reply to author
Forward
0 new messages