Pregunta Configurando jstree menú contextual contextual para diferentes tipos de nodos


He visto un ejemplo en algún lugar en línea que muestra cómo personalizar la apariencia del menú contextual del botón derecho de jstree (usando el complemento contextmenu).

Por ejemplo, permita a mis usuarios eliminar "documentos" pero no "carpetas" (ocultando la opción "eliminar" del menú contextual para las carpetas).

Ahora no puedo encontrar ese ejemplo. ¿Alguien puede señalarme en la dirección correcta? El oficial documentación realmente no ayudó.

Editar:

Como quiero el menú contextual predeterminado con solo uno o dos cambios menores, prefiero no recrear todo el menú (aunque, por supuesto, lo haré si es el único). Lo que me gustaría hacer es algo como esto:

"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'); 
            }
        }
    }
}

pero no funciona: el elemento de creación simplemente está deshabilitado (la alerta nunca aparece).


73
2017-12-30 02:24


origen


Respuestas:


los contextmenu el complemento ya tiene soporte para esto. De la documentación a la que vinculó:

items: Espera un objeto o una función, que debería devolver un objeto. Si se usa una función, se dispara en el contexto del árbol y recibe un argumento: el nodo al que se hizo clic con el botón derecho.

Entonces, en lugar de dar contextmenu un objeto codificado para trabajar, puede suministrar la siguiente función. Comprueba el elemento al que se hizo clic para una clase llamada "carpeta" y elimina el elemento de menú "eliminar" al eliminarlo del objeto:

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

Tenga en cuenta que lo anterior ocultará la opción de eliminar por completo, pero el complemento también le permite mostrar un elemento mientras deshabilita su comportamiento, al agregar _disabled: true al artículo relevante. En este caso puedes usar items.deleteItem._disabled = true dentro de if declaración en su lugar.

Debería ser obvio, pero recuerde inicializar el complemento con el customMenu función en lugar de lo que tenía anteriormente:

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

Editar: Si no desea que se vuelva a crear el menú en cada clic con el botón derecho, puede poner la lógica en el controlador de acción para la misma opción de menú de eliminación.

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

Editar de nuevo: Después de mirar el código fuente jsTree, parece que el menú contextual se está recreando cada vez que se muestra de todos modos (consulte el show() y parse() funciones), así que no veo un problema con mi primera solución.

Sin embargo, me gusta la notación que está sugiriendo, con una función como valor para _disabled. Un camino potencial para explorar es envolver sus parse() función con la suya que evalúa la función en disabled: function () {...} y almacena el resultado en _disabledantes de llamar al original parse().

No será difícil modificar su código fuente directamente. La línea 2867 de la versión 1.0-rc1 es la relevante:

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

Simplemente puede agregar una línea antes de esta que verifica $.isFunction(val._disabled), y de ser así, val._disabled = val._disabled(). A continuación, envíalo a los creadores como un parche :)


122
2018-01-05 02:19



Implementado con diferentes tipos de nodos:

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

Y la función de menú personalizado:

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

Funciona maravillosamente


17
2018-02-27 20:48



Para limpiar todo

En lugar de esto:

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

Utilizar esta:

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

12
2018-05-11 18:50



Sin embargo, he adaptado la solución sugerida para trabajar con tipos un poco diferente, tal vez puede ayudar a alguien más:

Donde # {$ id_arr [$ k]} es la referencia al contenedor div ... en mi caso utilizo muchos árboles para que todo este código sea el resultado para el navegador, pero entiendes la idea ... Básicamente quiero todo las opciones del menú contextual, pero solo 'Crear' y 'Pegar' en el nodo de Drive. Obviamente con los enlaces correctos a esas operaciones más adelante:

<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},

4
2018-06-16 21:51



Puede modificar el código de @ Box9 para adaptarlo a su requerimiento de desactivación dinámica del menú contextual como:

function customMenu(node) {

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


       items.deleteItem._disabled = true;
    }   

}  

Necesita agregar un atributo "xyz" en sus datos XML o JSOn


1
2017-10-22 12:49



a partir de jsTree 3.0.9 que necesitaba usar algo como

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

porque el node objeto que se proporciona no es un objeto jQuery.


0
2018-02-13 17:55