1

I have a jquery context menu on my landing page where I have hardcode menu items. Now I want to get the menu items from server. Basically the idea is to show file names in a specified directory in the context menu list and open that file when user clicks it...

This is so far I have reached..

***UPDATE***

C# code

[HttpPost]
        public JsonResult GetHelpFiles()
         {
            List<Manuals> manuals = null;

            var filesPath = Server.MapPath(@"\HelpManuals");

            var standardPath = new DirectoryInfo(filesPath);

            if (standardPath.GetFiles().Any())
            {
               manuals = standardPath.GetFiles().Select(x => new Manuals
               {
                    Name = GetFileNamewithoutExtension(x.Name),
                    Path = x.Name
                }).ToList();
            }

            return Json(manuals, JsonRequestBehavior.AllowGet);
        }

        private string GetFileNamewithoutExtension(string filename)
        {
            var extension = Path.GetExtension(filename);

           return filename.Substring(0, filename.Length - extension.Length);
        }

JavaScript Code

$.post("/Home/GetHelpFiles", function (data) {
    $.contextMenu({
        selector: '#helpIcon',
        trigger: 'hover',
        delay: 300,
        build: function($trigger, e) {
            var options = {
                callback: function(key) {
                    window.open("/HelpManuals/" + key);
                },
                items: {}
            };
            $.each(data, function (item, index) {
                console.log("display name:" + index.Name);
                console.log("File Path:" + index.Path);
                options.items[item.Value] = {
                    name: index.Name,
                    key: index.Path
                }
            });
        }
    });
});

Thanks to Matt. Now, the build function gets fire on hover.. but im getting illegal invocation... and when iterating through json result, index.Name and this.Name gives correct result. But item.Name doesn't give anything..

3
  • Can you add a console.log(data); right after the function (data) { ? Then let me know what is output in the console? Commented Jun 9, 2014 at 6:01
  • I Get NaN.. I have even tried JSON.parse(data).. no luck Commented Jun 9, 2014 at 6:26
  • I probably switched the each parameters. probably index, item where item.Name would then work. sorry about that Commented Jun 11, 2014 at 13:45

4 Answers 4

2

to add items to the context menu dynamically you need to make a couple changes

$.contextMenu({
    selector: '#helpIcon',
    trigger: 'hover',
    delay: 300,
    build: function($trigger, e){
        var options = {
            callback: function (key) {
                var manual;
                if (key == "adminComp") {
                    manual = "AdminCompanion.pdf";
                } else {
                    manual = "TeacherCompanion.pdf";
                }
                window.open("/HelpManuals/" + manual);
            },
            items: {}
        }

        //how to populate from model
        @foreach(var temp in Model.FileList){
            <text> 
                options.items[temp.Value] = {
                    name: temp.Name,
                    icon: 'open'
                }
            </text>
        }

        //should be able to do an ajax call here but I believe this will be called 
        //every time the context is triggered which may cause performance issues
        $.ajax({
            url: '@Url.Action("Action", "Controller")',
            type: 'get',
            cache: false,
            async: true,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (_result) {
            if (_result.Success) {
                $.each(_result, function(item, index){
                    options.items[item.Value] = {
                        name: item.Name,
                        icon: 'open'
                    }
                });
            }
        });
        return options;
    }

});

so you use build and inside of that define options and put your callback in there. The items defined in there is empty and is populated in the build dynamically. We build our list off of what is passed through the model but I believe you can put the ajax call in the build like I have shown above. Hopefully this will get you on the right track at least.

Sign up to request clarification or add additional context in comments.

1 Comment

Hi Matt, thanks for reply, I have updated my question with new code. I think im almost there....could u please comment on the error..
2

I solved this problem the following way.

On a user-triggered right-click I return false in the build-function. This will prevent the context-menu from opening. Instead of opeing the context-menu I start an ajax-call to the server to get the contextMenu-entries.

When the ajax-call finishes successfully I create the items and save the items on the $trigger in a data-property. After saving the menuItems in the data-property I open the context-menu manually.

When the build-function is executed again, I get the items from the data-property.

$.contextMenu({
    build: function ($trigger, e)
    {
        // check if the menu-items have been saved in the previous call
        if ($trigger.data("contextMenuItems") != null)
        {
            // get options from $trigger
            var options = $trigger.data("contextMenuItems");

            // clear $trigger.data("contextMenuItems"),
            // so that menuitems are gotten next time user does a rightclick 
            // from the server again.
            $trigger.data("contextMenuItems", null);
            return options;
        }
        else
        {
            var options = {
                callback: function (key)
                {
                    alert(key);
                },
                items: {}
            };
            $.ajax({
                url: "GetMenuItemsFromServer",
                success: function (response, status, xhr)
                {
                    // for each menu-item returned from the server
                    for (var i = 0; i < response.length; i++)
                    {
                        var ri = response[i];
                        // save the menu-item from the server in the options.items object
                        options.items[ri.id] = ri;
                    }
                    // save the options on the table-row;
                    $trigger.data("contextMenuItems", options);

                    // open the context-menu (reopen)
                    $trigger.contextMenu();
                },
                error: function (response, status, xhr)
                {
                    if (xhr instanceof Error)
                    {
                        alert(xhr);
                    }
                    else
                    {
                        alert($($.parseHTML(response.responseText)).find("h2").text());
                    }
                }
            });
            // This return false here is important                
            return false;
        }
   });

1 Comment

I've been working on a solution like yours all day today. I had to make some tweaks for my use case but it is a great solution. thank you
1

Here's my solution using deferred, important to know that this feature is supported for sub-menus only

$(function () {
            $.contextMenu({
                selector: '.SomeClass',
                build: function ($trigger, e) {
                    var options = {
                        callback: function (key, options) {
                            // some call back
                        },
                        items: JSON.parse($trigger.attr('data-storage')) //this is initial static menu from HTML attribute you can use any static menu here
                    };

                    options.items['Reservations'] = {
                            name: $trigger.attr('data-reservations'),
                            icon: "checkmark",
                            items: loadItems($trigger) // this is AJAX loaded submenu
                        };
                    return options;
                }
            });
        });
        
        // Now this function loads submenu items in my case server responds with 'Reservations' object
        var loadItems = function ($trigger) {
        var dfd = jQuery.Deferred();
        $.ajax({
            type: "post",
            url: "/ajax.php",
            cache: false,
            data: {
              // request parameters are not importaint here use whatever you need to get data from your server
                
            },
            success: function (data) {
                dfd.resolve(data.Reservations);
            }
        });
        return dfd.promise();
    };

Comments

0

I have finally found a better solution after reading jquery context menu documentation, thoroughly..

C# CODE

 public JsonResult GetHelpFiles()
         {
            List<Manuals> manuals = null;

            var filesPath = Server.MapPath(@"\HelpManuals");

            var standardPath = new DirectoryInfo(filesPath);

            if (standardPath.GetFiles().Any())
            {
               manuals = standardPath.GetFiles().Select(x => new Manuals
               {
                    Name = GetFileNamewithoutExtension(x.Name),
                    Path = x.Name
                }).ToList();
            }

            return Json(manuals, JsonRequestBehavior.AllowGet);
        } 

HTML 5

<div id="dynamicMenu">
                        <menu id="html5menu" type="context" style="display: none"></menu>
                    </div> 

JavaScript Code

$.post("/Home/GetHelpFiles", function (data) {
                $.each(data, function (index, item) {
                    var e = '<command label="' + item.Name + '" id ="' + item.Path + '"></command>';
                    $("#html5menu").append(e);
                });


                $.contextMenu({
                    selector: '#helpIcon',
                    trigger: 'hover',
                    delay: 300,
                    items: $.contextMenu.fromMenu($('#html5menu'))
                });
            });


 $("#dynamicMenu").on("click", "menu command", function () {
        var link = $(this).attr('id');
        window.open("/HelpManuals/" + link);
    });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.