Can a dynamic category have categories?

108 views
Skip to first unread message

Alan Smith

unread,
Jun 23, 2025, 11:29:22 AM6/23/25
to Blockly
I am trying to have a dynamic category that has categories, but it seems as soon as I put a category in it that nothing displays.   Here is a code snippet that fails:

public componentsFlyout(workspace: Blockly.WorkspaceSvg) {
const contents: toolboxItems.ContentsType[] = [];

contents.push(
{
kind: 'category',
name: 'sample',
categorystyle: 'logic_category',
contents: [{
kind: 'block',
type: 'controls_if',
},
]
}
);

const toolboxInfo = {
contents: contents,
};

return toolboxInfo;
}

I am trying to make a category that has sub-categories (which ones are determined dynamically) and each of those would have a set of blocks if there is a better way of doing this.

Thanks in advance,

Alan

Mark Friedman

unread,
Jun 23, 2025, 1:32:22 PM6/23/25
to blo...@googlegroups.com
Alan,

  If the only thing that you want to control dynamically is which categories (or sub-categories) appear in the toolbox then you might want to take a look at this from the Blockly docs. If you also need the contents to also be dynamic then you should take a look at this documentation on dynamic categories. Note that your code isn't quite the same as the documentation is showing.

  Hope this helps.

-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/dbd7b21a-f33b-45cf-b93d-84471efe7d59n%40googlegroups.com.

Alan Smith

unread,
Jun 23, 2025, 2:02:15 PM6/23/25
to blo...@googlegroups.com
What I am trying to do:
 - For each component on a robot (decided by the user), it will show the components available under a menu. (so not just hidden and shown, but they could have different names)

Imagine a robot that has an arm, and a drivetrain, and a wrist.   Your menu would look like:
Robot
  - Arm
       (blocks that go along with arm)
  - Drivetrain
       (blocks that go along with drivetrain)
 - Wrist
       (blocks that go along with wrist)

I think I have found that the call to get the menu doesn't occur until its parent is expanded (which means I can dynamically populate the json) and then for the blocks for each component I can use a combination of the dynamic category (which you mentioned) along with seeing which item in the toolbox has been selected last in order to populate it.   Does that seem like a reasonable way to do this?


For future consideration, what I would love is something like this:
{
  "kind": "category",
  "name": "My category",
  "custom": "MY_CUSTOM_CATEGORY",
  "extraState": { arg0 : "blah" },
},


where the extraState was passed into the flyoutCallback so you could make one instance of the callback that would populate itself differently based off of what was pased in.

Thanks,

Alan

P.S.  Mark, I heard that you were honored at the blockly summit.  Congratulations!!


Christopher Allen

unread,
Jun 23, 2025, 2:23:45 PM6/23/25
to blo...@googlegroups.com
Hello Alan,

There was a previous request to support subcategories in dynamic categories, but as you will see in the discussion on that issue it is not something we plan to support: the dynamic category callback is only expected to supply the contents of the flyout, and changes to the set of available cateogries are normally expected to be implemented via a call to updateToolbox.

I think I have found that the call to get the menu doesn't occur until its parent is expanded (which means I can dynamically populate the json) and then for the blocks for each component I can use a combination of the dynamic category (which you mentioned) along with seeing which item in the toolbox has been selected last in order to populate it.   Does that seem like a reasonable way to do this?

It's probably better not to rely on seeing which category/subcategory is selected, since AFAIK it's not guaranteed that the category with focus will always be the same as the category being used to populate the flyout.

For future consideration, what I would love is something like this:
{
  "kind": "category",
  "name": "My category",
  "custom": "MY_CUSTOM_CATEGORY",
  "extraState": { arg0 : "blah" },
},


where the extraState was passed into the flyoutCallback so you could make one instance of the callback that would populate itself differently based off of what was pased in.

That's an interesting suggestion.  I'm not sure about the extra state idea, but I do think that a feature request that we pass the category (+ subcategory/ies) name(s) to the dynamic category callback is something we might consider implementing.

In the meantime, if you have many dynamic categories that you want to use the same callback function for, but need some additional context information, you can do that using closures (function expressions)—e.g.:

// Returns an array of objects.
var fyoutCallback = function(workspace, info) {
  // ...Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
  return blockList;
};
// Associates the function with various strings.
myWorkspace.registerToolboxCategoryCallback(
  'CATEGORY_1', (workspace) => flyoutCallback(workspace, {arg0: 'blah'}));
myWorkspace.registerToolboxCategoryCallback(
  'CATEGORY_2', (workspace) => flyoutCallback(workspace, {arg0: 'blerg'}));


Best wishes,

Christohper
 

Alan Smith

unread,
Jun 23, 2025, 3:41:54 PM6/23/25
to blo...@googlegroups.com
Christopher,
   The closures is a great way to solve the second problem!! 

I was trying to copy the extraState idea from how the typical blocks do it, because that allows you to put more of things that need to be different in JSON instead of scattering them between JSON and typescript.    If you prefer, a third alternative that could work well would be to pass the toolboxitemid to the callback function.   This would get away from language issues of passing the name.

For the first one, I'll see if I can get updateToolbox to solve my problem.

Thanks,

Alan


--
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.

Christopher Allen

unread,
Jun 24, 2025, 4:42:50 AM6/24/25
to blo...@googlegroups.com
Hi Alan,

I was trying to copy the extraState idea from how the typical blocks do it, because that allows you to put more of things that need to be different in JSON instead of scattering them between JSON and typescript.    If you prefer, a third alternative that could work well would be to pass the toolboxitemid to the callback function.   This would get away from language issues of passing the name.

Yes, that's an excellent point.

If you have a moment would you open a feature request for this, including a motivating example?  No guarantees, of course, but it seems like it would be a simple but useful change that could be made in a backwards-compatible way, so I think it's worth our further consideration—and although it wouldn't be a priority for the team to implement it would be the sort of change for which we would be likely to accept a (well-written) PR from an external contributor such as yourself.


Best wishes,

Christopher

Alan Smith

unread,
Jun 25, 2025, 2:46:10 PM6/25/25
to blo...@googlegroups.com
I put this in as a feature request: https://github.com/google/blockly/issues/9168   
Please let me know of things here that aren't clear.

Not being familiar with contributing to Blockly, should I wait for team discussion on it before starting to work on a PR?   I think that this should be fairly straightforward, but often things that are thorny look that way before starting on them.   Or as the saying goes, "We do these things not because they are easy, but because we thought they were going to be easy when we started them."

Thanks,

Alan


--
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.

Christopher Allen

unread,
Jun 26, 2025, 9:41:23 AM6/26/25
to blo...@googlegroups.com
Hi Alan,

I put this in as a feature request: https://github.com/google/blockly/issues/9168   
Please let me know of things here that aren't clear.

Thanks.  I had a read, and it looks good but I would suggest including an example like the one from your previous message:

{
  "kind": "category",
  "name": "My category",
  "custom": "MY_CUSTOM_CATEGORY",
  "extraState": { arg0 : "blah" },
},

(but maybe with a more concrete example related to your robots context).


Not being familiar with contributing to Blockly, should I wait for team discussion on it before starting to work on a PR?

For bug fixes PRs are almost always welcome (though of course we do not guarantee to accept them) but for a feature request, especially one that involves making API changes, I would recommend waiting to see what happens when the issue is triaged (normally we do this most Fridays).

I think that this should be fairly straightforward, but often things that are thorny look that way before starting on them.   Or as the saying goes, "We do these things not because they are easy, but because we thought they were going to be easy when we started them."

One of the things we appreciate about external contributors is that they don't yet know what's hard, and so are not as afraid of diving in, as those of us who have come to know the codebase better.  :-)


Christopher

Alan Smith

unread,
Jun 27, 2025, 1:21:43 PM6/27/25
to blo...@googlegroups.com
For the record of anyone else following along, the blockly team reviewed the feature request today and decided it would not be supported since it could already be done with closures (as Christopher mentioned above.)

Sadly,

Alan

--
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.
Reply all
Reply to author
Forward
0 new messages