How to use a context menu with FancyTree

There used to be several different ways, and even an almost official way to have a context menu in a FancyTree. But it has long been decommissioned, and now the recommendation is to use a global context menu, instead of having a context menu only for the FancyTree. That does make sense, since if your application is already using a context menu library, there may be some inconsistencies with another library provided by FancyTree. Good engineering decision.

Requirements

In Nestor QA, we are using Bower to manage some of the project dependencies. The now recommended library for context menus, that is compatible with the FancyTree library, is jquery-ui-contextmenu (by the same author). However, I could not find it via bower search. So the first step is to install it using a GitHub tag.

bower install --save ui-contextmenu=mar10/jquery-ui-contextmenu#84fb56c6760d1ebd8be3785cd6ff5b67c5c41453

Where 84fb56c6760d1ebd8be3785cd6ff5b67c5c41453 is the commit hash for the latest tag, v1.13.0.

Furthermore, as we also have RequireJS, we have to add the following entry under the paths entry.

paths: {
    'jquery-ui/menu': 'libs/jquery-ui/ui/widgets/menu'
},

packages: [
    {
        name: 'uicontextmenu',
        location: 'libs/ui-contextmenu/',
        main: 'jquery.ui-contextmenu.min'
    }
],

map: {
    'jquery-ui/menu': {
        'keycode': 'libs/jquery-ui/ui/keycode',
        'position': 'libs/jquery-ui/ui/position',
        'safe-active-element': 'libs/jquery-ui/ui/safe-active-element',
        'unique-id': 'libs/jquery-ui/ui/unique-id',
        'version': 'libs/jquery-ui/ui/version',
        'widget': 'libs/jquery-ui/ui/widget'
    }
}

You need to add a package as the ui-contextmenu requires jquery-ui/menu, and adding a path doesn’t seem to work correctly in this case. But you do need to add a path for the jquery-ui/menu dependency.

Finally, jquery-ui/menu requires several other dependencies, that you can just add a map entry for RequireJS, and that’s it.

Creating the navigation tree

We are using FancyTree for the navigation tree, so the code may look a bit more complex than necessary, but if you abstract the logic for the Nestor QA application, you can see that it consists only of a delegate property, which is the CSS filter for elements that trigger the context menu; the menu items, and a select function invoked when you - as you correctly deduced - select a menu item.

// context menu
el.contextmenu({
    delegate: ".editable",
    menu: [
        {
            title: "Edit", 
            cmd: "edit", 
            uiIcon: "ui-icon-pencil"
        },
        {
            title: "Delete",
            cmd: "delete",
            uiIcon: "ui-icon-trash"
        }
    ],
    select: function(event, ui) {
        var nodeId = ui.target[0].parentNode.parentNode.id;
        // fancy tree HTML elements have the key as ft_$KEY
        var underscoreIndex = nodeId.indexOf('_');
        if (underscoreIndex > 0) {
            var length = nodeId.length;
            var key = nodeId.substring(underscoreIndex+1, length);
            var node = el.fancytree('getNodeByKey', key);
            var href = node.data.href;
            if (ui.cmd == 'edit') {
                var editHref = href.substring(0, href.length - 5);
                Backbone.history.navigate(editHref, {
                    trigger: false
                });
            } else if (ui.cmd == 'delete') {
                var editHref = href.substring(0, href.length - 5);
                var deleteHref = editHref + '/confirmDelete';
                Backbone.history.navigate(deleteHref, {
                    trigger: false
                });
            } else {
                console.log('Command ' + ui.cmd + ' not recognized!');
            }
        }
    }
});

But nevertheless the el.fancytree('getNodeByKey', key); call may be useful for you as well! Read more about the ui-context menu API in their Wiki page.

Context menu in action
Context menu in action

Happy hacking!