विभिन्न नोड प्रकारों के लिए jstree राइट-क्लिक संदर्भमेनू को कॉन्फ़िगर करना


85

मैंने एक उदाहरण कहीं ऑनलाइन दिखाया है कि jstree के राइट-क्लिक संदर्भ मेनू (सन्दर्भ मेन्यू प्लगइन का उपयोग करके) को कैसे अनुकूलित किया जाए।

उदाहरण के लिए, मेरे उपयोगकर्ताओं को "दस्तावेज़" हटाने की अनुमति दें, लेकिन "फ़ोल्डर" नहीं (फ़ोल्डर के लिए संदर्भ मेनू से "हटाएं" विकल्प छिपाकर)।

अब मुझे वह उदाहरण नहीं मिल रहा है। क्या कोई मुझे सही दिशा दिखा सकता है? आधिकारिक दस्तावेज ने वास्तव में मदद नहीं की।

संपादित करें:

चूंकि मैं केवल एक या दो मामूली बदलावों के साथ डिफ़ॉल्ट संदर्भ मेनू चाहता हूं, इसलिए मैं पूरे मेनू को फिर से बनाना नहीं चाहूंगा (हालांकि निश्चित रूप से मैं अगर यह एकमात्र तरीका है)। मैं क्या करना चाहूंगा कुछ इस तरह है:

"contextmenu" : {
    items: {
        "ccp" : false,
        "create" : {
            // The item label
            "label" : "Create",
            // The function to execute upon a click
            "action": function (obj) { this.create(obj); },
            "_disabled": function (obj) { 
                alert("obj=" + obj); 
                return "default" != obj.attr('rel'); 
            }
        }
    }
}

लेकिन यह काम नहीं करता है - बनाएं आइटम हमेशा अक्षम होता है (अलर्ट कभी प्रकट नहीं होता है)।

जवाबों:


144

contextmenuप्लगइन पहले से ही इस के लिए समर्थन हासिल है। आपके द्वारा लिंक किए गए दस्तावेज़ से:

items: किसी ऑब्जेक्ट या फ़ंक्शन की अपेक्षा करता है, जिसे ऑब्जेक्ट वापस करना चाहिए । यदि किसी फ़ंक्शन का उपयोग किया जाता है, तो इसे ट्री के संदर्भ में निकाल दिया जाता है और एक तर्क प्राप्त होता है - वह नोड जो राइट क्लिक किया गया था।

तो contextmenuसाथ काम करने के लिए एक हार्ड-कोडेड ऑब्जेक्ट देने के बजाय , आप निम्नलिखित फ़ंक्शन की आपूर्ति कर सकते हैं। यह उस तत्व की जांच करता है जिसे "फ़ोल्डर" नामक वर्ग के लिए क्लिक किया गया था, और ऑब्जेक्ट को हटाकर "हटाएं" मेनू आइटम को हटा देता है:

function customMenu(node) {
    // The default set of all items
    var items = {
        renameItem: { // The "rename" menu item
            label: "Rename",
            action: function () {...}
        },
        deleteItem: { // The "delete" menu item
            label: "Delete",
            action: function () {...}
        }
    };

    if ($(node).hasClass("folder")) {
        // Delete the "delete" menu item
        delete items.deleteItem;
    }

    return items;
}

ध्यान दें कि ऊपर डिलीट विकल्प को पूरी तरह से छिपा देगा, लेकिन प्लगइन आपको _disabled: trueसंबंधित आइटम को जोड़कर, उसके व्यवहार को अक्षम करते हुए एक आइटम दिखाने की अनुमति देता है । इस मामले में आप उपयोग कर सकते हैं items.deleteItem._disabled = trueके भीतर ifबयान के बजाय।

स्पष्ट होना चाहिए, लेकिन customMenuआप पहले क्या था के बजाय फ़ंक्शन के साथ प्लगइन को इनिशियलाइज़ करना याद रखें :

$("#tree").jstree({plugins: ["contextmenu"], contextmenu: {items: customMenu}});
//                                                                    ^
// ___________________________________________________________________|

संपादित करें: यदि आप नहीं चाहते हैं कि मेनू को हर राइट-क्लिक पर फिर से बनाया जाए, तो आप तर्क मेनू को हटाए गए मेनू आइटम के लिए खुद ही डाल सकते हैं।

"label": "Delete",
"action": function (obj) {
    if ($(this._get_node(obj)).hasClass("folder") return; // cancel action
}

फिर से संपादित करें: jsTree स्रोत कोड को देखने के बाद, ऐसा लगता है कि संदर्भमेनू को हर बार फिर से बनाया जा रहा है, वैसे भी ( कार्यों show()और parse()कार्यों को देखें ), इसलिए मुझे अपने पहले समाधान के साथ कोई समस्या नहीं दिख रही है।

हालाँकि, मैं उस नोटेशन को पसंद करता हूं जो आप के लिए मान के रूप में एक फ़ंक्शन के साथ सुझा रहे हैं _disabled। एक संभावित मार्ग का पता लगाने के लिए parse()अपने स्वयं के साथ अपने फ़ंक्शन को लपेटना है जो मूल में कॉल करने से पहले फ़ंक्शन का मूल्यांकन करता है disabled: function () {...}और परिणाम को स्टोर करता है।_disabledparse()

उनके सोर्स कोड को सीधे संशोधित करना मुश्किल नहीं होगा। संस्करण 1.0-rc1 की लाइन 2867 प्रासंगिक है:

str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins ";

आप बस इस से पहले एक लाइन जोड़ सकते हैं जो जांच करता है $.isFunction(val._disabled), और यदि हां, तो val._disabled = val._disabled()। फिर इसे पैच के रूप में रचनाकारों को भेजें :)


धन्यवाद। मैंने सोचा था कि मैंने एक बार एक समाधान देखा जिसमें केवल डिफ़ॉल्ट से बदलने की आवश्यकता थी (खरोंच से पूरे मेनू को फिर से बनाने के बजाय)। यदि बाउंटी की समय सीमा समाप्त होने से पहले कोई बेहतर समाधान नहीं है, तो मैं इस उत्तर को स्वीकार करूंगा।
एमजीओवेन

@MGOwen धारणात्मक मैं कर रहा हूँ "डिफ़ॉल्ट" को संशोधित करने के लिए, लेकिन हाँ आप रहे हों तो सही है कि वस्तु फिर से बनाया हर बार समारोह में कहा जाता है हो जाता है। हालाँकि, डिफ़ॉल्ट को पहले क्लोन करने की आवश्यकता होती है, अन्यथा डिफ़ॉल्ट स्वयं संशोधित होता है (और आपको मूल स्थिति पर वापस लौटने के लिए अधिक जटिल तर्क की आवश्यकता होगी)। एक विकल्प जिसके बारे में मैं सोच सकता हूँ कि यह var itemsकेवल एक बार बनाया गया फ़ंक्शन के बाहर जाने के लिए है, और समारोह से आइटम का एक चयन लौटाता है, जैसे return {renameItem: items.renameItem};याreturn {renameItem: items.renameItem, deleteItem: items.deleteItem};
डेविड तांग

मुझे वह आखिरी पसंद है विशेष रूप से, जहां आप jstree स्रोत को संशोधित करते हैं। मैंने इसकी कोशिश की और यह काम करता है, "_disabled" (मेरे उदाहरण में) को दिया गया फ़ंक्शन चलता है। लेकिन, यह मदद नहीं करता है क्योंकि मैं नोड को एक्सेस नहीं कर सकता (मुझे कम से कम इसकी आवश्यकता है कि फ़ंक्शन के दायरे में से नोड नोड्स को फ़िल्टर करने के लिए यह विशेषता है)। मैंने उन चर का निरीक्षण करने की कोशिश की, जिन्हें मैं jstree स्रोत कोड से पास कर सकता था, लेकिन नोड को नहीं खोज सका। कोई विचार?
MGOwen

@MGOwen, यह उस <a>तत्व की तरह दिखता है जिस पर क्लिक किया गया था $.vakata.context.tgt। इसलिए देखने का प्रयास करें $.vakata.context.tgt.attr("rel")
डेविड तांग

1
jstree में 3.0.8: if ($(node).hasClass("folder")) काम नहीं किया। लेकिन यह किया: if (node.children.length > 0) { items.deleteItem._disabled = true; }
रयान वेटर्स

19

विभिन्न नोड प्रकारों के साथ कार्यान्वित किया गया:

$('#jstree').jstree({
    'contextmenu' : {
        'items' : customMenu
    },
    'plugins' : ['contextmenu', 'types'],
    'types' : {
        '#' : { /* options */ },
        'level_1' : { /* options */ },
        'level_2' : { /* options */ }
        // etc...
    }
});

और customMenu फ़ंक्शन:

function customMenu(node)
{
    var items = {
        'item1' : {
            'label' : 'item1',
            'action' : function () { /* action */ }
        },
        'item2' : {
            'label' : 'item2',
            'action' : function () { /* action */ }
        }
    }

    if (node.type === 'level_1') {
        delete items.item2;
    } else if (node.type === 'level_2') {
        delete items.item1;
    }

    return items;
}

खूबसूरती से काम करता है।


1
मैं इस उत्तर को पसंद करता हूं क्योंकि यह typejQuery का उपयोग करके प्राप्त सीएसएस वर्ग के बजाय विशेषता पर निर्भर करता है ।
बेनी बोटेमा

आप 'action': function () { /* action */ }दूसरे स्निपेट में किस कोड को डाल रहे हैं ? क्या होगा यदि आप "सामान्य" कार्यक्षमता और मेनू आइटम का उपयोग करना चाहते हैं, लेकिन बस उनमें से एक को हटा दें (जैसे हटाएं हटाएं लेकिन नाम बदलें और बनाएं रखें)? मेरे विचार में, यह वही है जो ओपी वैसे भी पूछ रहा था। निश्चित रूप से आपको Rename और Create जैसी चीजों के लिए कार्यक्षमता को फिर से लिखने की आवश्यकता नहीं है यदि आप किसी अन्य आइटम को हटाएं जैसे हटाएं?
एंडी

मुझे यकीन नहीं है कि मैं आपके सवाल को समझ सकता हूँ। आप itemsऑब्जेक्ट की सूची में पूर्ण संदर्भ मेनू (उदाहरण के लिए, हटाएं, नाम बदलें और बनाएं) के लिए सभी कार्यक्षमता को परिभाषित कर रहे हैं , फिर आप फ़ंक्शन node.typeके अंत में दिए गए इन मदों को हटाने के लिए निर्दिष्ट करते customMenuहैं। जब उपयोगकर्ता दिए गए नोड पर क्लिक करता है type, तो संदर्भ मेनू customMenuफ़ंक्शन के अंत में सशर्त में हटाए गए सभी आइटम को सूचीबद्ध करेगा । आप किसी भी कार्यक्षमता को फिर से नहीं लिख रहे हैं (जब तक कि तीन साल पहले इस जवाब के बाद से jstree नहीं बदला है, इस मामले में यह अब प्रासंगिक नहीं हो सकता है)।
खड़ी

12

सब कुछ साफ करने के लिए।

इसके अलावा:

$("#xxx").jstree({
    'plugins' : 'contextmenu',
    'contextmenu' : {
        'items' : { ... bla bla bla ...}
    }
});

इसे इस्तेमाल करो:

$("#xxx").jstree({
    'plugins' : 'contextmenu',
    'contextmenu' : {
        'items' : customMenu
    }
});

5

मैंने कुछ अलग प्रकार के साथ काम करने के लिए सुझाए गए समाधान को अनुकूलित किया है हालांकि, शायद यह किसी और की मदद कर सकता है:

जहां # {$ id_arr [$ k]] डिव कंटेनर का संदर्भ है ... मेरे मामले में मैं कई पेड़ों का उपयोग करता हूं इसलिए यह सभी कोड ब्राउज़र में आउटपुट होगा, लेकिन आपको यह विचार मिलता है .. मूल रूप से मुझे सभी चाहिए संदर्भ मेनू विकल्प लेकिन ड्राइव नोड पर केवल 'बनाएँ' और 'चिपकाएँ'। जाहिर है कि बाद में उन कार्यों के लिए सही बाइंडिंग के साथ:

<div id="$id_arr[$k]" class="jstree_container"></div>
</div>
</li>
<!-- JavaScript neccessary for this tree : {$value} -->
<script type="text/javascript" >
jQuery.noConflict();
jQuery(function ($) {
// This is for the context menu to bind with operations on the right clicked node
function customMenu(node) {
    // The default set of all items
    var control;
    var items = {
        createItem: {
            label: "Create",
            action: function (node) { return { createItem: this.create(node) }; }
        },
        renameItem: {
            label: "Rename",
            action: function (node) { return { renameItem: this.rename(node) }; }
        },
        deleteItem: {
            label: "Delete",
            action: function (node) { return { deleteItem: this.remove(node) }; },
            "separator_after": true
        },
        copyItem: {
            label: "Copy",
            action: function (node) { $(node).addClass("copy"); return { copyItem: this.copy(node) }; }
        },
        cutItem: {
            label: "Cut",
            action: function (node) { $(node).addClass("cut"); return { cutItem: this.cut(node) }; }
        },
        pasteItem: {
            label: "Paste",
            action: function (node) { $(node).addClass("paste"); return { pasteItem: this.paste(node) }; }
        }
    };

    // We go over all the selected items as the context menu only takes action on the one that is right clicked
    $.jstree._reference("#{$id_arr[$k]}").get_selected(false, true).each(function (index, element) {
        if ($(element).attr("id") != $(node).attr("id")) {
            // Let's deselect all nodes that are unrelated to the context menu -- selected but are not the one right clicked
            $("#{$id_arr[$k]}").jstree("deselect_node", '#' + $(element).attr("id"));
        }
    });

    //if any previous click has the class for copy or cut
    $("#{$id_arr[$k]}").find("li").each(function (index, element) {
        if ($(element) != $(node)) {
            if ($(element).hasClass("copy") || $(element).hasClass("cut")) control = 1;
        }
        else if ($(node).hasClass("cut") || $(node).hasClass("copy")) {
            control = 0;
        }
    });

    //only remove the class for cut or copy if the current operation is to paste
    if ($(node).hasClass("paste")) {
        control = 0;
        // Let's loop through all elements and try to find if the paste operation was done already
        $("#{$id_arr[$k]}").find("li").each(function (index, element) {
            if ($(element).hasClass("copy")) $(this).removeClass("copy");
            if ($(element).hasClass("cut")) $(this).removeClass("cut");
            if ($(element).hasClass("paste")) $(this).removeClass("paste");
        });
    }
    switch (control) {
        //Remove the paste item from the context menu
        case 0:
            switch ($(node).attr("rel")) {
                case "drive":
                    delete items.renameItem;
                    delete items.deleteItem;
                    delete items.cutItem;
                    delete items.copyItem;
                    delete items.pasteItem;
                    break;
                case "default":
                    delete items.pasteItem;
                    break;
            }
            break;
            //Remove the paste item from the context menu only on the node that has either copy or cut added class
        case 1:
            if ($(node).hasClass("cut") || $(node).hasClass("copy")) {
                switch ($(node).attr("rel")) {
                    case "drive":
                        delete items.renameItem;
                        delete items.deleteItem;
                        delete items.cutItem;
                        delete items.copyItem;
                        delete items.pasteItem;
                        break;
                    case "default":
                        delete items.pasteItem;
                        break;
                }
            }
            else //Re-enable it on the clicked node that does not have the cut or copy class
            {
                switch ($(node).attr("rel")) {
                    case "drive":
                        delete items.renameItem;
                        delete items.deleteItem;
                        delete items.cutItem;
                        delete items.copyItem;
                        break;
                }
            }
            break;

            //initial state don't show the paste option on any node
        default: switch ($(node).attr("rel")) {
            case "drive":
                delete items.renameItem;
                delete items.deleteItem;
                delete items.cutItem;
                delete items.copyItem;
                delete items.pasteItem;
                break;
            case "default":
                delete items.pasteItem;
                break;
        }
            break;
    }
    return items;
$("#{$id_arr[$k]}").jstree({
  // List of active plugins used
  "plugins" : [ "themes","json_data", "ui", "crrm" , "hotkeys" , "types" , "dnd", "contextmenu"],
  "contextmenu" : { "items" : customMenu  , "select_node": true},

2

Btw: यदि आप मौजूदा संदर्भ मेनू से विकल्प निकालना चाहते हैं - तो यह मेरे लिए काम कर रहा है:

function customMenu(node)
{
    var items = $.jstree.defaults.contextmenu.items(node);

    if (node.type === 'root') {
        delete items.create;
        delete items.rename;
        delete items.remove;
        delete items.ccp;
    }

    return items;
}


1

आप संदर्भ मेनू के गतिशील अक्षम करने की अपनी आवश्यकता के अनुरूप @ Box9 कोड को संशोधित कर सकते हैं:

function customMenu(node) {

  ............
  ................
   // Disable  the "delete" menu item  
   // Original // delete items.deleteItem; 
   if ( node[0].attributes.yyz.value == 'notdelete'  ) {


       items.deleteItem._disabled = true;
    }   

}  

आपको अपने XML या JSOn डेटा में एक विशेषता "xyz" जोड़ने की आवश्यकता है


1

के रूप में jsTree 3.0.9 मुझे कुछ का उपयोग करने की आवश्यकता थी

var currentNode = treeElem.jstree('get_node', node, true);
if (currentNode.hasClass("folder")) {
    // Delete the "delete" menu item
    delete items.deleteItem;
}

क्योंकि जो nodeवस्तु प्रदान की गई है वह jQuery की वस्तु नहीं है।


1

डेविड की प्रतिक्रिया ठीक और कुशल लगती है। मुझे समाधान का एक और रूप मिला है जहां आप विभिन्न नोड्स को अलग करने के लिए a_attr विशेषता का उपयोग कर सकते हैं और इसके आधार पर आप अन्य संदर्भ मेनू उत्पन्न कर सकते हैं।

नीचे दिए गए उदाहरण में, मैंने दो प्रकार के नोड्स फ़ोल्डर और फ़ाइलों का उपयोग किया है। मैंने ग्लिफ़िकॉन का उपयोग करके विभिन्न आइकन भी उपयोग किए हैं। फ़ाइल प्रकार नोड के लिए, आप केवल नाम बदलने और हटाने के लिए संदर्भ मेनू प्राप्त कर सकते हैं। फ़ोल्डर के लिए, सभी विकल्प हैं, फ़ाइल बनाएँ, फ़ोल्डर बनाएँ, नाम बदलें, निकालें।

पूर्ण कोड स्निपेट के लिए, आप https://everyething.com/Example-of-jsTree-with-different-context-menu-for-different-node-type देख सकते हैं

 $('#SimpleJSTree').jstree({
                "core": {
                    "check_callback": true,
                    'data': jsondata

                },
                "plugins": ["contextmenu"],
                "contextmenu": {
                    "items": function ($node) {
                        var tree = $("#SimpleJSTree").jstree(true);
                        if($node.a_attr.type === 'file')
                            return getFileContextMenu($node, tree);
                        else
                            return getFolderContextMenu($node, tree);                        
                    }
                }
            });

प्रारंभिक जसन डेटा नीचे दिया गया है, जहां नोड प्रकार a_attr के भीतर उल्लेख किया गया है।

var jsondata = [
                           { "id": "ajson1", "parent": "#", "text": "Simple root node", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
                           { "id": "ajson2", "parent": "#", "text": "Root node 2", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
                           { "id": "ajson3", "parent": "ajson2", "text": "Child 1", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
                           { "id": "ajson4", "parent": "ajson2", "text": "Child 2", icon: 'glyphicon glyphicon-folder-open', "a_attr": {type:'folder'} },
            ];

फ़ाइल और फ़ोल्डर बनाने के लिए contect मेनू आइटम के भाग के रूप में फ़ाइल कार्रवाई के रूप में नीचे दिए गए समान कोड का उपयोग करें।

action: function (obj) {
                                $node = tree.create_node($node, { text: 'New File', icon: 'glyphicon glyphicon-file', a_attr:{type:'file'} });
                                tree.deselect_all();
                                tree.select_node($node);
                            }

फ़ोल्डर कार्रवाई के रूप में:

action: function (obj) {
                                $node = tree.create_node($node, { text: 'New Folder', icon:'glyphicon glyphicon-folder-open', a_attr:{type:'folder'} });
                                tree.deselect_all();
                                tree.select_node($node);
                            }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.