Field value gets overwritten by flyout value

99 views
Skip to first unread message

Bart Butenaers

unread,
Jun 12, 2021, 3:12:43 AM6/12/21
to Blockly
Hello everybody,

I think I made a very stupid mistake somewhere, but can see where...

In my toolbox.xml file I add this block to my category:
<xml xmlns="http://www.w3.org/1999/xhtml" id="toolbox" style="display: none;">
  <category name="Timers" colour="#BB8FCE">
    <block type="testblock">
    </block>
  </category>
</xml>

In the definition of my block, I specify that I field value needs to be "inFlyOut" or "inWorkspace":

Blockly.Blocks['testblock'] = {
    init: function() {
        this.jsonInit({
            "type": "testblock",
            "message0": "%1",
            "args0": [
                {
                    "type": "field_input",
                    "name": "NAME",
                    "text": (this.isInFlyout) ? "inFlyOut" : "inWorkspace"
                }
            ],
            "inputsInline": false,
            "previousStatement": null,
            "nextStatement": null,
            "colour": "#BB8FCE",
            "tooltip": "",
            "helpUrl": null
        });
    }
};

When I open the flyout, then the field value is correctly filled in the json:

blockly1.png
And it is correctly displayed in the flyout:

blockly2.png
And when I drag it from my flyout to my workspace, the field value is again correctly filled in my json:

blockly3.png
This value is correctly passed through the Blockly coding until the applyFieldTagNodes function is being executed, which gets this first parameter:

blockly5.png
And this second parameter:
blockly7.png
 After that function call, the field value is overwritten by the incorrect value:
blockly4.png
And then it appears incorrectly in my workspace:
blockly6.png
I assume I have forgotten something in my xml or ...?

Thanks in advance!!
Bart

Beka Westberg

unread,
Jun 14, 2021, 12:53:00 PM6/14/21
to blo...@googlegroups.com
Hi Bart!

Well this is definitely something new! Could you explain more about what you're trying to achieve? It seems like the inFlyout vs inWorkspace text might be a minimal example for something else?

I think that it might be more stable if you approach this problem from a different direction. Generally for JSON block definitions it is assumed that the JSON contains pure data.  That is, strings, numbers,  bools, etc - no functions or ternaries. If you want to add extra logic to your block (like determining if it is in a flyout or workspace) it's probably better to do that via a block extension, or an events listener.

But speaking specifically to determining inFlyout vs inWorkspace, I think the issue is that when you drag a block from the flyout, it exactly copies the state of the block in the flyout. For example, if I edit a block before dragging it out, that edited value will stick:
flyout-copy.gif

I think you might be able to reset the value by listening to create events because events are fired a few ms after they occur. But I'm honestly not sure :/

I hope that gives you some places to start! If you have any further questions please reply!
--Beka

--
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 on the web visit https://groups.google.com/d/msgid/blockly/48774e05-da79-459d-af00-933da501758cn%40googlegroups.com.

Bart Butenaers

unread,
Jun 15, 2021, 5:30:46 PM6/15/21
to Blockly
Hi Beka,

Thanks a lot for sharing your insights!!

1) Why I need this.  The ioBroker team has developed very nice Javascript timer blocks, and I would like to have similar blocks in my node-red-contrib-blockly repository.  As soon as you drag a timer block in the workspace, the timer should get a unique id.  So I the flyout you see e.g. the value "timeout", but it the workspace it should become "timout_0", "timout_1", ...

timer.png

2) It makes sense what you say that the json state is copied.  That is probably where my problem started.  The guys from ioBroker have done it like this (as you can see here):

timer2.png

Since I always use the JSON notation, I had converted this code to JSON.  So I assume it works fine in their case, because they don't use JSON (if I understand your explanation correctly)...

3) Apologies but I don't understand how I could solve this with extensions or event listeners ;-(  Could you please explain this a bit more please?

Bart 
Message has been deleted

Beka Westberg

unread,
Jun 15, 2021, 8:36:25 PM6/15/21
to Blockly
Hi Bart :D

> Why I need this. The ioBroker team has developed very nice Javascript timer blocks...

Those do look really nice! And thank you for the additional context, that's really helpful for thinking about the best way to achieve what you're looking for =)

> Could you please explain this a bit more please?

Yeah definitely! So I think what you'd want to do is add an event listener that listens for block create events. These events are fired whenever a block is created. This includes dragging from the flyout, loading from a save, being re-created via undo, etc. When you get an event that is associated with your specific timer block, you'll be able to modify the field value so that it has the unique name that you want.

I think it would look something like this:
```
var myListener = function(event) {
  if (event.type != Blockly.Events.BLOCK_CREATE) {
    return; // we only care about create events.
  }

  for (var i = 0; i < event.ids.length; i++) { // .ids lists the ids of all of the blocks that were created during this event.
    var block = myWorkspace.getBlockById(event.ids[i]);
    if (block.type == 'MY_TIMER_TYPE') {
      block.setFieldValue(myGenNameFunc(), 'TIMER_NAME_FIELD_NAME');
    }
  }
}

myWorkspace.addChangeListener(myListener);
```

That code is completely untested, and some of the function names may be wrong, but that's the basic idea.

I hope that helps (and works)! If you have any further questions please reply :D
--Beka

Bart Butenaers

unread,
Jun 16, 2021, 11:17:34 AM6/16/21
to Blockly
Hello Beka,

Top support!  Couldn't have accomplished it without your help ...

Thanks to your example code, it became clear which way I had to solve it.
Will share my code here, so perhaps somebody else can learn from it.

Since my workspace can changed continiously (due to a normal mode / full screen mode setup), I wanted separate the code:
  1. Add the listener to the workspace, where the workspace is created.  
  2. Execute the listener handler functionality inside my block definitions, to have the block related logic inside the block definition.
To accomplish that, I have added an oncreate function to my blocks:

Blockly.Blocks['set_timeout'] = {
    init: function() {
        this.jsonInit({
            "type": "set_timeout",
            "message0": Blockly.Msg.SET_TIMEOUT,
            "args0": [
                {
                    "type": "field_input",
                    "name": "NAME",
                    "text": findLegalName(Blockly.Msg.SET_TIMOUT_NAME, this),
                    "spellcheck": false
                }
            ]
            "inputsInline": false,
            "previousStatement": null,
            "nextStatement": null,
            "colour": "#BB8FCE",
            "tooltip": Blockly.Msg.SET_TIMEOUT_TOOLTIP,
            "helpUrl": null
        });
    },
    oncreated: function() {
        var prefix = Blockly.Msg.SET_TIMOUT_NAME;
        var newUniqueName = getUniqueName(prefix, this.type, this.workspace)
        this.setFieldValue(newUniqueName, 'NAME');
    }
};

That oncreated function should be called after this block has been created.
To do that, the block functions are being triggered in the event handler (which is registered where the workspace is created):

        // Create the workspace
        myWorkspace = Blockly.inject('blocklyDiv', {...  });
        
        // Change listener that calls the oncreate function of a block, after the block has been created in the workspace
         myWorkspace.addChangeListener(function(event) {
            if (event.type != Blockly.Events.BLOCK_CREATE) {
                return; // we only care about create events.
            }

            for (var i = 0; i < event.ids.length; i++) { // .ids lists the ids of all of the blocks that were created during this event.
                var block = node.workspace.getBlockById(event.ids[i]);
                if (block.oncreated && typeof block.oncreated === 'function') {
                    block.oncreated();
                }
            }
        });

And that seems to be working fine:

blockly_timer_ids.gif

Have a nice day!!!
Bart

Bart Butenaers

unread,
Jun 16, 2021, 2:44:25 PM6/16/21
to Blockly
BTW for completeness, here are the functions being used to get a new unique name (that is not being used yet in the workspace):

function getUniqueName(prefix, blockType, workspace) {
    var counter = 1;
    
    var existingNames = getAllNames(blockType, workspace);
    
    while(true) {
        var newName = prefix + "_" + counter;
        
        if (!existingNames.includes(newName)) {
            return newName;
        }
        
        counter++;
    }
}

function getAllNames(blockType, workspace) {
    var blocks = workspace.getAllBlocks();
    var names = [];

    for (var i = 0; i < blocks.length; i++) {
        if (blocks[i].type === blockType) {
            var name = blocks[i].getFieldValue('NAME');
            names.push(name);
        }
    }
    
    return names;
}

Beka Westberg

unread,
Jun 17, 2021, 9:40:54 AM6/17/21
to blo...@googlegroups.com
Thank you so much for posting the code you're using! It's always great to stumble upon an old post and see there's complete code hehe. I'm sure this will help someone out in the future :D

Best wishes,
--Beka

--
You received this message because you are subscribed to a topic in the Google Groups "Blockly" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/blockly/BB8lgBwGMqg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to blockly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/blockly/21aa5a0c-e12f-4875-a058-eef682146582n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages