Hi,
I try to create Menu from JSON, but it doesn't work and I don't know how to do it.
This is my JSON:
var menu= [
{type: "Menu", params: {targetNodeIds:["bodyAttachPoint"], id:"bodyAttachPoint"}, children: [
{type: "MenuItem", params: {label:"Simple menu item 1"}},
{type: "MenuItem", params: {label:"Simple menu item 2"}},
{type: "MenuSeparator"},
{type: "MenuItem", params: {label:"Simple menu item 3"}, children: [
{type: "MenuItem", params: {label:"Simple menu item 4"}},
{type: "MenuItem", params: {label:"Simple menu item 5"}}
]}
]}
];
{type: "Menu", params: {targetNodeIds:["bodyAttachPoint"], id:"bodyAttachPoint"}, children: [
{type: "MenuItem", params: {label:"Simple menu item 1"}},
{type: "MenuItem", params: {label:"Simple menu item 2"}},
{type: "MenuSeparator"},
{type: "MenuItem", params: {label:"Simple menu item 3"}, children: [
{type: "MenuItem", params: {label:"Simple menu item 4"}},
{type: "MenuItem", params: {label:"Simple menu item 5"}}
]}
]}
];
and this is my function:
function createWidgets(menu){
var widgets;
dojo.forEach(data,
function(oneEntry, index, array) {
var widget = new dijit[oneEntry.type](oneEntry.params);
console.debug(oneEntry.type + " at index " + index);
if(oneEntry.children){
widget.addChild(createWidgets(oneEntry.children));
}
return widget;
}
widgets.startup();
);
var widgets;
dojo.forEach(data,
function(oneEntry, index, array) {
var widget = new dijit[oneEntry.type](oneEntry.params);
console.debug(oneEntry.type + " at index " + index);
if(oneEntry.children){
widget.addChild(createWidgets(oneEntry.children));
}
return widget;
}
widgets.startup();
);
It doesn't work, but I hope that idea is clear.
You can change JSON scructure and function too.
How can I create widgets, not only Menu, but some Layout widgets or dojox widgets form one JSON?
Thanks

Hello there. No offense, but
Hello there.
No offense, but your code was wrong in very many ways... However, your question posed a very interesting problem to me, so I decided to take a stab at it, and I hope this is helpful to you or to anybody else looking for a solution similar to your problem. I may be wrong about any of the following points (if anybody else knows better than I about any of these points, please let me know for I would love to know more :) but here are some of the problems I found with your code along with a quick explanation of the solutions I applied:
In its current form, createWidgets cannot take and process a list of widgets while still using recursion. There may be a way to make that functionality work, but I found it much easier to process one widget at a time recursively in order to be able to process each child widget properly. So I have added a forEach for each child widget and removed the outer forEach, and added a forEach loop in the calling code to correctly process potentially more than one menu widget in the original list.
Every widget needs a div; I added a dynamically created div to each of the types passed in to createWidgets.
As far as I can tell, every dijit widget needs to be created specifically. I would be thrilled to hear that there is such a creation technique as 'var widget = new dijit[oneEntry.type]({});' but as far as I know, you have to actually name the widget type to be created. So I added an if...else block to create each type of widget based on the Type specified.
Finally, it is my understanding that sub-menu items containing MenuItems themselves require a PopupMenuItem widget containing a Menu widget containing the various MenuItems. I figured I would leave that up to you to implement (note that I think that the sub-menu Menu must be added to the PopupMenuItem via the .popup method, not the .addChild method).
My calling code:
var menu= [ {type: "Menu", params: {targetNodeIds:["bodyAttachPoint"]}, children: [ {type: "MenuItem", params: {label:"Simple menu item 1"}}, {type: "MenuItem", params: {label:"Simple menu item 2"}}, {type: "MenuSeparator"}, // {type: "Menu", params: {label:"Simple menu item 3"}, children: [ {type: "MenuItem", params: {label:"Simple menu item 4"}}, {type: "MenuItem", params: {label:"Simple menu item 5"}} // ]} ]} ]; dojo.forEach(menu, function(oneEntry, index, array) { var widgetChild = createWidgets(oneEntry); } );The createWidgets function:
function createWidgets(oneEntry){ var widget = null; var div = document.createElement('div'); if(oneEntry.type=="Menu"){ widget = new dijit.Menu(oneEntry.params, div); } else if(oneEntry.type=="MenuItem"){ widget = new dijit.MenuItem(oneEntry.params, div); } else if(oneEntry.type=="MenuSeparator"){ widget = new dijit.MenuSeparator(oneEntry.params, div); } else{ alert('invalid type: '+oneEntry.type); return; } dojo.forEach(oneEntry.children, function(child, index, array) { var widgetChild = createWidgets(child); widget.addChild(widgetChild); } ); return widget; }Note that this code will probably not work for just any dojo widget in the way you asked about. Each different type of widget might require particular function calls and other tweaks to the code...
However, my code above does indeed dynamically build a single-level right-click menu list from a json structure, so I hope it is helpful to somebody :)
Interesting tour de force, but are we trying too hard?
In looking over the whole JSON => Dijit link, I too have lamented the lack of a "create this dynamic widget from"("this JSON struct") type of facility. There are indeed ways to get there, but they are rather involved, which makes me wonder, why the interest in JSON over templating technologies?
with popupMenuItem
I included a possible solution to include PopupMenuItem:
function createWidgetsTree(oneEntry){ var widget = null; var popget = null; var div = document.createElement('div'); if(oneEntry.type=="Menu"){ widget = new dijit.Menu(oneEntry.params, div); } else if(oneEntry.type=="MenuItem"){ widget = new dijit.MenuItem(oneEntry.params, div); } else if(oneEntry.type=="MenuSeparator"){ widget = new dijit.MenuSeparator(oneEntry.params, div); } else if(oneEntry.type=="PopupMenuItem"){ widget = new dijit.Menu(oneEntry.params, div); popget = 1; } else{ alert('invalid type: '+oneEntry.type); return; } dojo.forEach(oneEntry.children, function(child, index, array) { var widgetChild = createWidgetsTree(child); widget.addChild(widgetChild); } ); // test if we created a popup menu if ( popget != null ){ return new dijit.PopupMenuItem({ label: oneEntry.params["label"] , popup:widget }); } return widget; }In this case data for menu should be:
var menu = [ {type: "Menu", params: {targetNodeIds:["bodyAttachPoint"]}, children: [ {type: "MenuItem", params: {label:"Simple menu item 1"}}, {type: "MenuItem", params: {label:"Simple menu item 2"}}, {type: "MenuSeparator"}, {type: "PopupMenuItem", params: {label:"Simple menu item 3"}, children: [ {type: "MenuItem", params: {label:"Simple menu item 4"}}, {type: "MenuItem", params: {label:"Simple menu item 5"}} ]} ]} ];I can't see expand arrow icon
Thanks for this guide first.
After I used above code, I could see context menu. But expand arrow icon that has child sub menu does not displayed correctly.
I could see arrow icons when I used below test sample. Am I missing something ?
function fClick() {alert("clicked!")};
pMenu = new dijit.Menu({targetNodeIds:["prog_menu"], id:"progMenu"});
pMenu.addChild(new dijit.MenuItem({label:"Programmatic Context Menu", disabled:true}));
pMenu.addChild(new dijit.MenuSeparator());
pMenu.addChild(new dijit.MenuItem({label:"Simple menu item", onClick:fClick}));
pMenu.addChild(new dijit.MenuItem({label:"Another menu item", onClick:fClick}));
pMenu.addChild(new dijit.MenuItem({label:"With an icon", iconClass:"dijitEditorIcon dijitEditorIconCut", onClick:fClick}));
var mItem = new dijit.MenuItem({label:"dojo.event clicking"});
dojo.connect(mItem, "onClick", function(){alert("click! handler created via dojo.connect()")});
pMenu.addChild(mItem);
var pSubMenu = new dijit.Menu({parentMenu:pMenu, id:"progSubMenu"});
pSubMenu.addChild(new dijit.MenuItem({label:"Submenu item", onClick:fClick}));
pSubMenu.addChild(new dijit.MenuItem({label:"Submenu item", onClick:fClick}));
pMenu.addChild(new dijit.PopupMenuItem({label:"Submenu", popup:pSubMenu, id:"progPopupMenuItem"}));
pMenu.startup();
Ploblem was solved.
After adding menu.startup() to above solution, I could see right expand arrow icon.
I could dynamically show popupMenu from json which is made by dojo.xhrPost with above solution.
Thanks.
Creating a toolbar of menus
I have completed above code to make a toolbar of menus, while also staying able to build contextual menu.
Below example is working in my app, but here, the CSS don't fire for some unknown reason.
Please note the manner to obtain a contextual menu valid for the whole window.
Style to be imported, adapt for your directories
Code in the 'head' section
dojo.require("dijit.dijit"); dojo.require("dijit.Toolbar"); dojo.require("dijit.Menu"); dojo.require("dijit.form.Button"); // menus definitions var menulist= [ {type: "TlbMenu", params:{label:"Menu1"}, children:[ {type: "MenuItem",params:{label:"Menu1 item1", onClick:fct1, id:"M1It1"}}, {type: "MenuItem",params:{label:"Menu1 item2", onClick:fct2, id:"M1It2"}}, {type: "MenuSeparator"}, {type: "PopupMenuItem", params:{label:"Menu1 item3"}, children:[ {type: "MenuItem",params:{label:"Menu1 item3 sub1"}}, {type: "MenuItem",params:{label:"Menu1 item3 sub2"}} ]}, {type: "MenuItem",params:{label:"Menu1 item4"}} ]}, {type: "TlbMenu", params:{label:"Menu2"}}, {type: "TlbMenu", params:{label:"Menu3"}}, ]; var menu2= [ {type: "Menu", params:{contextMenuForWindow:true}, children:[ {type: "MenuItem",params:{label:"Context item1", onClick:fct3}}, {type: "MenuItem",params:{label:"Context item2"}}, {type: "MenuItem",params:{label:"Context item3"}} ]} ]; function createWidgets(oneEntry, node){ var widget = null; var popget= null; var div = document.createElement('div'); if(oneEntry.type=="TlbMenu"){ widget = new dijit.Menu({}, div); widget.startup(); //display arrow for submenu var button = new dijit.form.DropDownButton({ label: oneEntry.params["label"], dropDown:widget }); node.addChild(button); } else if(oneEntry.type=="Menu"){ widget = new dijit.Menu(oneEntry.params, div); // widget.bindDomNode(node); alternative to bind to only one node } else if(oneEntry.type=="MenuItem"){ widget = new dijit.MenuItem(oneEntry.params, div); } else if(oneEntry.type=="PopupMenuItem"){ widget = new dijit.Menu(oneEntry.params, div); popget=1; } else if(oneEntry.type=="MenuSeparator"){ widget = new dijit.MenuSeparator(oneEntry.params, div); } else{ alert('invalid type: '+oneEntry.type); return; } dojo.forEach(oneEntry.children, function(child) { var widgetChild = createWidgets(child); widget.addChild(widgetChild); } ); if (popget!=null){ return new dijit.PopupMenuItem({label:oneEntry.params["label"], popup:widget}); } return widget; } function cre_menu(menu,node){ dojo.forEach(menu, function(oneEntry) {var widgetChild = createWidgets(oneEntry, node);} ); } //test functions function fct1() { alert('menu1, item 1, disabling item2'); dijit.byId("M1It2").setDisabled(true); // use dijit.byId, not dojo.byId ! } function fct2() {alert('menu1, item 2');} function fct3() {alert('Context menu, item 1');} dojo.addOnLoad(function(){ // build HTML var tlbar = new dijit.Toolbar({}, "tl1"); cre_menu (menulist, tlbar); cre_menu (menu2); // how to fire the widgets startup to get css ?? });and in the body