Deserialized workspace is visually correct, but non-functonal (connectionDB)

209 views
Skip to first unread message

Eric Gradman

unread,
Mar 31, 2022, 7:57:08 PM3/31/22
to Blockly
Hi.

I've built a Vue app which contains a Blockly workspace inside a Vue component.  I have a save button, which serializes the workspace and sends it off to an API for storage.  Later, I reload the page and the serialized workspace is loaded into the workspace.

The reloaded workspace looks correct (the blocks are recreated, and in the right places). But when I try to drag the blocks or connect new blocks I get an error (in Chrome). 

I've tried this with the last v7 blockly, and with the v8 released today (!).

The error:
blockly_compressed.js?e98f:1136 Uncaught Error: Unable to find connection in connectionDB.
    at Proxy.module$exports$Blockly$ConnectionDB.ConnectionDB.removeConnection (blockly_compressed.js?e98f:1136:1)
    at module$exports$Blockly$RenderedConnection.RenderedConnection.moveTo (blockly_compressed.js?e98f:1042:1)
    at module$exports$Blockly$RenderedConnection.RenderedConnection.moveBy (blockly_compressed.js?e98f:1043:1)
    at BlockSvg.module$exports$Blockly$BlockSvg.BlockSvg.moveConnections (blockly_compressed.js?e98f:1097:1)
    at BlockDragger.module$exports$Blockly$BlockDragger.BlockDragger.updateBlockAfterMove_ (blockly_compressed.js?e98f:665:1)
    at BlockDragger.module$exports$Blockly$BlockDragger.BlockDragger.endDrag (blockly_compressed.js?e98f:663:1)
    at module$exports$Blockly$TouchGesture.TouchGesture.module$exports$Blockly$Gesture.Gesture.handleUp (blockly_compressed.js?e98f:689:1)
    at module$exports$Blockly$TouchGesture.TouchGesture.handleUp (blockly_compressed.js?e98f:1152:1)
    at HTMLDocument.h (blockly_compressed.js?e98f:78:1)


Example serialization
{
  "blocks": {
    "blocks": [
      {
        "type": "show",
        "x": 337,
        "y": 113,
        "id": "KuTZ4m8p]`Acf;=:2cve",
        "fields": {
          "type": "",
          "table": ""
        }
      }
    ],
    "languageVersion": 0
  }
}

How I'm deserializing the workspace
 mounted() {
   var options = this.$props.options || {};
   if (!options.toolbox) {
    options.toolbox = this.$refs["blocklyToolbox"];
   }
   this.workspace = Blockly.inject(this.$refs["blocklyDiv"], options);
   Blockly.serialization.workspaces.load(this.data, this.workspace);
 }


In this case, "this.data" is the JSON object shown above, and is supplied as a prop by a higher-level object.  This code is the only modification I've made to the blocklyComponent.vue file from https://github.com/google/blockly-samples/tree/master/examples/blockly-vue

Eric Gradman

unread,
Mar 31, 2022, 8:11:25 PM3/31/22
to Blockly
update: the behavior is exactly the same when (de)serializing with XML instead of JSON

Beka Westberg

unread,
Apr 1, 2022, 10:53:04 AM4/1/22
to blo...@googlegroups.com
Hello!

Thank you for bringing this to our attention. That is definitely a bad bug. Would you be able to send in a minimal example (for example, a modification of the existing blockly-vue example code) so that the core team could reproduce and debug the issue?

Best wishes,
--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/da3a55fa-807c-44e0-ac53-fd5debc3e912n%40googlegroups.com.

Eric Gradman

unread,
Apr 3, 2022, 2:05:15 PM4/3/22
to Blockly
Sure.


I transplanted the code from the blockly-vue example into a modern `vue-cli` project and upgraded the dependencies.  Instructions to repro are in the README.

Thanks for taking a look!

Beka Westberg

unread,
Apr 4, 2022, 4:51:51 PM4/4/22
to blo...@googlegroups.com
Thank you so much for providing the sample! That was really helpful.

So I think the problem is that the WorkspaceSvg instance is being made reactive. IE everything is being wrapped in Proxies. So inside `removeConnection`, there's a point where a Connection in an array is expected to be `===` to another Connection, but it doesn't work because the Connection *in* the array is a Proxy, while the Connection *not in* the array is not a Proxy.

Sadly I've never used Vue before, so I don't know if that's an accurate analysis. Does it sound correct to you?

If it is the issue, I'll have to investigate more into how we can make the WorkspaceSvg non-reactive. Or at least protect parts of it from being Proxy-ified.

Best wishes,
--Beka

Beka Westberg

unread,
Apr 4, 2022, 5:18:54 PM4/4/22
to blo...@googlegroups.com
Filed #6062 to track this issue if you're interested in following along =)

Eric Gradman

unread,
Apr 5, 2022, 11:53:19 AM4/5/22
to Blockly
Hey Beka,

Your hypothesis is totally correct.  The `workspace` proxy is created because it's declared in `data()`.  But workspace doesn't need to be reactive because we're don't want to re-render the whole component when the workspace changes.

The super-easy fix is just to remove workspace from `data`.  We can still assign `this.workspace = Blockly.inject(...)` we just won't be using the proxy assignment.  I pushed a `fixed` branch to my repo that demonstrates that, but whatever.


Thanks for the diagnosis!

Beka Westberg

unread,
Apr 8, 2022, 11:47:43 AM4/8/22
to blo...@googlegroups.com
Awesome! Thank you for checking this out for me. And sorry for the late reply :/

I put up a pull request with your fix, and merged it into the Vue sample. So hopefully integrating with Vue will be easier for future people =)

Thank you so much for working with me, testing this, and pushing that fix to your repo. You were incredibly helpful and I sincerely appreciate it!

The Bestest of Wishes,
--Beka

Reply all
Reply to author
Forward
0 new messages