Problems with dynamic blocks and dropdowns

76 views
Skip to first unread message

Matteo Mannai

unread,
Dec 11, 2024, 11:15:30 AMDec 11
to Blockly
Hi guys, i'm relatively new to blockly community and i'm trying different stuffs with blocks, but now i found a problem i can't get over it.
I've created a block that has a first dropdown element, showing some column names (dropdown elements are generated dynamically here), a second dropdown showing static operators, and a third part that is an input or another dropdown depending on the value selected on the first dropdown. 
The block kinda works correctly but the first and the third dropdown render is weird, i'm going to attach a screenshot to show how it renders now.
1 and 2 are correct renders that happen sometimes, and sometimes the 3rd render shows up, breaking the entire component.

This is my code right now:

Blockly.Blocks["logic_equality"] = {
    init: function () {
      this.columnDropdown = new Blockly.FieldDropdown(
        this.generateColumnOptions.bind(this),
        this.onColumnChange.bind(this)
      );

      this.columnInput = this.appendDummyInput("A").appendField(
        this.columnDropdown,
        "columnDropdown"
      );

      this.operatorInput = this.appendDummyInput().appendField(
        new Blockly.FieldDropdown([
          ["=", "EQ"],
          ["≠", "NQ"],
        ]),
        "OP"
      );

      this.dynamicInput = null;

      this.setOutput(true, "condition");
      this.setColour("#5C81A6");
      this.setTooltip("Confronta una variabile con un valore.");
      this.setHelpUrl("");
      this.setInputsInline(true);
    },

    generateColumnOptions: function () {
      if (!allColumns || allColumns.length === 0) {
        return [["", '""']];
      }

      return allColumns.map((element) => {
        return [element[0], element[1]];
      });
    },

    onColumnChange: function (selectedColumn) {
      const currentColumn = this.getFieldValue("columnDropdown");
      if (selectedColumn === currentColumn) {
        return;
      }

      if (this.dynamicInput) {
        this.removeInput(this.dynamicInput.name);
        this.dynamicInput = null;
      }
      if (this.getInput("B")) {
        this.removeInput("B");
      }

      const matchingField = valueList.find(
        (field) => field.field_name === selectedColumn
      );

      try {
        if (matchingField && matchingField.values.length > 0) {
          this.dynamicInput = this.appendDummyInput(
            "dynamicValueInput"
          ).appendField(
            new Blockly.FieldDropdown(
              matchingField.values.map((value) => [value, value]),
              (newValue) => {
                this.setFieldValue(newValue, "dynamicValue");
              }
            ),
            "dynamicValue"
          );
        } else {
          this.dynamicInput = this.appendValueInput("B").setCheck([
            "Number",
            "String",
            "Date",
            "db_null",
          ]);
        }

        this.initSvg();
        this.render();
      } catch (error) {
        console.error("Errore durante l'aggiornamento del blocco:", error);
      }
    },
  };

I also receive a   Maximum call stack size exceeded
when i try to move blocks in the workspace

I'm aware that the code has probably more than one problem but i can't figure them out, feeel free to blast my code :)
Thank you for your patience and sorry for my bad english!
3.png
1.png
2.png

Mark Friedman

unread,
Dec 11, 2024, 1:41:15 PMDec 11
to blo...@googlegroups.com
Hi, Matteo,

  I'm not sure what the rendering issue is, but I have an idea of why you might be getting the "stack size exceeded" error when you move your block.  I believe it might be caused by the code below:

                this.dynamicInput = this.appendDummyInput(
           "dynamicValueInput"
          ).appendField(
            new Blockly.FieldDropdown(
              matchingField.values.map((value) => [value, value]),
              (newValue) => {
                this.setFieldValue(newValue, "dynamicValue");
              }
            ),
            "dynamicValue"
          ); 

You'll note that the code creates a dropdown field whose validator sets the field's value.  However that setting of the value will cause the validator to be called which will create a new dropdown field ...  The result being a potentially infinite loop, which only ends when the JavaScript stack gets too large.  

It's not entirely clear to me why it only happens when the block is moved but it might have to do with the fact that when you move the block it will create new new "shadow" versions of the block in order to do show possible connection points and you may be moving the block near those possible connection points.  The creation of these particular new versions of the blocks may have just the right conditions for triggering the code above.

I any case, the solution would be to simply not have a validator function in the above code.  You don't need that if you just want to allow the value to be set to the selected value.

Also note that I believe that you shouldn't need the calls to initSvg and render.  See, for example, this section of the Blockly documentation about dropdown validators, where the example is somewhat similar to what you are doing.

-Mark


--
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/052097c6-3bfc-4de2-934f-9604d5f8e934n%40googlegroups.com.

Matteo Mannai

unread,
Dec 12, 2024, 12:09:15 PM (14 days ago) Dec 12
to Blockly
Hi Mark,
Thank you for your answer! I managed to fix the   Maximum call stack size exceeded  error, and now al least my workspace donesn't crash as soon as i place it 
However, I still continue to get the "rendering issue" problem, i'll try to explain it as best as i can.
When i click on the first dropdown (and sometimes also on the third dropdown if the block has it) the small dialog to choose the options is not placed directly under the block, but it is misplaced and it is placed at the top of the screen, even outside the  <BlocklyWorkspace/> component!

This is how it shoud normally render:
correct render.png

and this is how it render sometimes:
wrong render.png

The problem is that it is not only a graphical problem, when i click the misplaced dropdown, the selection doesn't change! 

This is my code right now:

Blockly.Blocks["logic_equality"] = {
    validate: function (newValue) {
      this.getSourceBlock().updateBlockShape(newValue);
      return newValue;
    },

    init: function () {
      this.columnDropdown = this.generateColumnOptions.bind(this);

      this.appendDummyInput("A").appendField(
        new Blockly.FieldDropdown(this.columnDropdown, this.validate),
        "columnDropdown"
      );
      this.appendDummyInput().appendField(
        new Blockly.FieldDropdown([
          ["=", "EQ"],
          ["≠", "NQ"],
        ]),
        "OP"
      );

      this.setOutput(true, "condition");
      this.setColour("#5C81A6");
      this.setTooltip("Confronta una variabile con un valore.");
      this.setHelpUrl("");
      this.setInputsInline(true);
    },

    generateColumnOptions: function () {
      if (!allColumns || allColumns.length === 0) {
        return [["", '""']];
      }

      return allColumns.map((element) => {
        return [element[0], element[1]];
      });
    },

    generateValueOptions: function () {
      if (!valueList || valueList.length === 0) {
        return [["", '""']];
      }

      return valueList.map((value, index) => {
        return [value.values[index], value.values[index]];
      });
    },

    updateBlockShape: function (newValue) {
      this.removeInput("freeInput", true);
      this.removeInput("guidedInput", true);

      if (valueList.some((item) => item.field_name === newValue)) {
        this.valuesDropdown = this.generateValueOptions.bind(this);

        this.appendDummyInput("guidedInput").appendField(
          new Blockly.FieldDropdown(this.valuesDropdown),
          "guidedInput"
        );
      }
    },
  };

Any idea how to fix this annoying problem?
Thank you for your patience and your help!

Mark Friedman

unread,
Dec 12, 2024, 6:25:57 PM (13 days ago) Dec 12
to blo...@googlegroups.com
Matteo,

  I'm afraid that I am having trouble replicating your rendering error.  Can you tell me which version of Blockly you are using, as well as which browser and device you are testing on?  Also, can you provide your definitions of allColumns and valueList?  I created definitions which seemed to make sense to me, but maybe didn't really match what you are actually using well enough.  Finally, are you seeing any errors in your browser's console when you are seeing the bad rendering?

-Mark


Matteo Mannai

unread,
Dec 13, 2024, 11:32:20 AM (13 days ago) Dec 13
to Blockly
Hi Mark,
thank for your reply!

I'll answer you questions immediately! 

I'm using:
  • blockly 11.1.1
  • react-blockly 9.0.0
  • @blockly/field-date 9.0.9 (but that's not used in the interested part of code)
I'm testing my code on:
  • Google Chrome  v.131.0.6778.140 browser
  • Lenovo V15 F4 IRU laptop
For the variables: 
  • allColumn is a 2 dimensional array where each element is an array of 2 elements 
           Something like this: 
           [ ["val1", "val1"], [val2", "val2"], ... ]

  • valueList is a arrray of objects where each object has this structure:
            {
                  field_id: //number
                  field_name: //name
                  values: ["val5", "val6", "val7"]
            }

In the console i get 0 errors or warning, one of major problems is also that i can't replicate the error consistently, sometimes i get the render error, sometimes not.
I tried to slightly change the code but nothing changed. Need some help!

Mark Friedman

unread,
Dec 13, 2024, 2:32:26 PM (13 days ago) Dec 13
to blo...@googlegroups.com
Matteo,

  I'll look further into it, but can you also check to see if you see any rendering errors if you use your blocks in the context of a more vanilla Blockly app?  Maybe just use the create-package script, and add your blocks.  That would help to reduce dependencies.  If that's too difficult, or if you don't see the rendering error in the simpler version, perhaps you can give us (or just me, if you like) access to your repo so that we can debug this in a context where it fails.

-Mark




Matteo Mannai

unread,
Dec 16, 2024, 11:23:04 AM (10 days ago) Dec 16
to Blockly
Solved!
I was re-creating and updating the block each time the dropdown value changed and this probably caused conflict in the block's rendering. I removed the updating part and re-created only the "dynamic" part of the block, now it seems to work correctly. Thank you for the patience and the help!
Reply all
Reply to author
Forward
0 new messages