Login Register

Creating dojo widgets programmatically from JSON

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"}}
  ]}
]}
];

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();
);

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.