jsTree, how to load child nodes on demand

0

I am using jsTree (3.3.2) to show a hierarchical structure of entities. The way I have it configured is to load the json with the complete structure taking as root node the node to which the authenticated user belongs, but it really is not functional for me; On the server side, by means of a recursive function, I generate the json which implies making multiple queries to the database. I need your help to configure the function so that it can differentiate the load of the root node, from the call to the load of the child nodes (direct to the root node). I have seen implementations in the pluggin page, but they make the differentiation of the root node by setting the value of the id = '#' and I do not think it serves me, since I need to have the id of each of the nodes to complete the rest of the views .

This is my current code, I have removed the configuration section from the context menu.

$('#arbol').jstree({
        'core': {
            'strings': {
                'Loading ...': 'Cargando ...'
            },
            'force_text': true,
            'themes': {
                'responsive': true,
                'variant': 'small',
                'stripes': false
            },
            'multiple': false,
            'animation': 135,
            'expand_selected_onload': false,
            'worker': true,
            'dblclick_toggle': true,
            'data': {
                'url': Routing.generate('app_renderArbol')
            },
            'check_callback': function(o, n, p, i, m) {
                // Esta funcion chequea el tipo de nodo seleccionado para evitar se convierta en draggable
            },
        },
        'plugins': ["state", "dnd", "contextmenu"],
        'state': {'key': 'arbol_state'},
        'dnd': {inside_pos: "last"}
    }).on('move_node.jstree', function(e, data) {
        e.preventDefault();
        $.ajax({
            datatypes: 'GET',
            method: 'JSON',
            url: Routing.generate('estructuraOrganizativa_moverCMF', {'cmf': data.node.id, 'gbt': data.parent}),
            beforeSend: function() {
                $('#indicador').addClass('cargando-satisfactorio').fadeIn('fast');
            }
        }).done(function(data) {
            $('#indicador').html(data.message);
        }).fail(function(jqXHR) {
            $('#indicador').addClass('cargando-error').html(jqXHR.responseText);
            data.instance.refresh();
        }).always(function(jqXHR, type) {
            if (type !== 'success') {
                $('#indicador').fadeOut(5500, function() {
                    $(this).removeClass('cargando-error').html("<span class='glyphicon glyphicon-time'></span> Cargando...");
                });
            } else {
                $('#indicador').fadeOut(4500, function() {
                    $(this).removeClass('cargando-error').html("<span class='glyphicon glyphicon-time'></span> Cargando...");
                });
            }
        });
    }).on('select_node.jstree', function(event, data) {
        event.preventDefault();
        if (data && data.selected && data.selected.length) {
            var nodo = data.instance.get_selected(true)[0];

            cargarVistaCentral(nodo); // esta trae de vuelta la vista central de la app basándose en el nodo seleccionado

        } else {
            return false;
        }
    });

These are the functions in the controller to return the json array (an action and a recursive function). Of course if you can properly implement the js, the driver would be much simpler: get the root node from the data of the authenticated user, and the rest of the nodes children (direct children) simpler to obtain: $nodo->getChildren(true) , well, it's already part of the model.

public function renderArbolAction(Request $request)
    {
        if ($request->isXmlHttpRequest())
        {
            $estructuraOrganizativa = $this->getUser()->getEstructuraOrganizativa();

            $arbol[] = array(
                'id' => $estructuraOrganizativa->getId(),
                'text' => $estructuraOrganizativa->getTitle(),
                'icon' => 'icon-sitemap', // \AppBundle\Utils\UI::getIconForTreeNode($estructuraOrganizativa)
                'data' => array(
                    'tipoEstructura' => array(
                        'id' => $estructuraOrganizativa->getTipoEstructuraOrganizativa()->getId(),
                        'nombre' => $estructuraOrganizativa->getTipoEstructuraOrganizativa()->getNombre(),
                    ),
                    'parent_id' => '#',
                    'rol_id' => $this->getUser()->getRol()->getId(),
                ),
                'state' => array(
                    'opened' => true,
                    'selected' => false
                ),
                'li_attr' => array(
                    'title' => $estructuraOrganizativa->getTitle(),
                ),
                ' a_attr' => array(
                    'href' => '#',
                ),
                'children' => $this->BuildChildsRecursive($estructuraOrganizativa),
            );

            $response = new Response(json_encode($arbol));
            $response->headers->set('Content-Type', 'application/json');

            return $response;
        } else
        {
            throw $this->createNotFoundException("Recurso no encontrado");
        }
    }

private function BuildChildsRecursive(EstructuraOrganizativa $nodo)
    {
        $nodosParaJSON = array();
        $repositoryEstructuraOrganizativa = $this->getDoctrine()->getRepository('AppBundle:EstructuraOrganizativa');
        $cantidadHijos = $repositoryEstructuraOrganizativa->childCount($nodo, true);

        if ($cantidadHijos === 0)
        {
            return array();
        } else
        {
            $hijos = $repositoryEstructuraOrganizativa->children($nodo, true);

            foreach ($hijos as $hijo)
            {
                if (in_array($hijo->getTipoEstructuraOrganizativa()->getId(), array(7, 8)))
                {
                    if (!$this->get('security.authorization_checker')->isGranted(array('ROLE_ESP_NAC', 'ROLE_ESP_PROV', 'ROLE_CONS_NAC', 'ROLE_CONS_PROV')))
                    {
                        continue;
                    }
                }
                $nodosParaJSON[] = array(
                    'id' => $hijo->getId(),
                    'text' => $hijo->getTitle(),
                    'icon' => \AppBundle\Utils\UI::getIconForTreeNode($hijo),
                    'data' => array(
                        'tipoEstructura' => array(
                            'id' => $hijo->getTipoEstructuraOrganizativa()->getId(),
                            'nombre' => $hijo->getTipoEstructuraOrganizativa()->getNombre(),
                        ),
                        'parent_id' => $hijo->getParent()->getId(),
                        'rol_id' => $this->getUser()->getRol()->getId(),
                    ),
                    'state' => array(
                        'opened' => false,
                        'selected' => false
                    ),
                    'li_attr' => array(
                        'title' => $hijo->getTitle(),
                    ),
                    ' a_attr' => array(
                        'href' => '#',
                    ),
                    'children' => $this->BuildChildsRecursive($hijo),
                );
            }

            return $nodosParaJSON;
        }
    }
    
asked by Francisco 01.04.2017 в 17:43
source

1 answer

1

Well, I found the solution and it is based on the comparison of the id of the node with the character '#' as I saw in the official documentation; Basically my solution is based on having two urls from where the json comes, waiting for jsTree to generate the tree.

$('#arbol').jstree({
        'core': {
            'strings': {
                'Loading ...': 'Cargando ...'
            },
            'force_text': true,
            'themes': {
                'responsive': true,
                'variant': 'small',
                'stripes': false
            },
            'multiple': false,
            'animation': 135,
            'expand_selected_onload': false,
            'worker': true,
            'dblclick_toggle': true,
            'data': {
                'url': function(node){
                     return node.id === '#' ? Routing.generate('app_renderArbolRaiz') : Routing.generate('app_renderArbolHijos', {'id': node.id});
                }
            },
            'check_callback': function(o, n, p, i, m) {
                // Esta funcion chequea el tipo de nodo seleccionado para evitar se convierta en draggable
            },
        },
        'plugins': ["state", "dnd", "contextmenu"],
        'state': {'key': 'arbol_state'},
        'dnd': {inside_pos: "last"}
    }).on('move_node.jstree', function(e, data) {
        e.preventDefault();
        $.ajax({
            datatypes: 'GET',
            method: 'JSON',
            url: Routing.generate('estructuraOrganizativa_moverCMF', {'cmf': data.node.id, 'gbt': data.parent}),
            beforeSend: function() {
                $('#indicador').addClass('cargando-satisfactorio').fadeIn('fast');
            }
        }).done(function(data) {
            $('#indicador').html(data.message);
        }).fail(function(jqXHR) {
            $('#indicador').addClass('cargando-error').html(jqXHR.responseText);
            data.instance.refresh();
        }).always(function(jqXHR, type) {
            if (type !== 'success') {
                $('#indicador').fadeOut(5500, function() {
                    $(this).removeClass('cargando-error').html("<span class='glyphicon glyphicon-time'></span> Cargando...");
                });
            } else {
                $('#indicador').fadeOut(4500, function() {
                    $(this).removeClass('cargando-error').html("<span class='glyphicon glyphicon-time'></span> Cargando...");
                });
            }
        });
    }).on('select_node.jstree', function(event, data) {
        event.preventDefault();
        if (data && data.selected && data.selected.length) {
            var nodo = data.instance.get_selected(true)[0];

            cargarVistaCentral(nodo); // esta trae de vuelta la vista central de la app basándose en el nodo seleccionado

        } else {
            return false;
        }
    });

and on the server side the two actions that correspond to each routing:

public function renderArbolRaizAction(Request $request)
    {
        if ($request->isXmlHttpRequest())
        {
            $arbol = array();
            $estructuraOrganizativa = $this->getUser()->getEstructuraOrganizativa();

            $arbol[] = array(
                'id' => $estructuraOrganizativa->getId(),
                'text' => $estructuraOrganizativa->getTitle(),
                'icon' => \AppBundle\Utils\UI::getIconForTreeNode($estructuraOrganizativa),
                'data' => array(
                    'tipoEstructura' => array(
                        'id' => $estructuraOrganizativa->getTipoEstructuraOrganizativa()->getId(),
                        'nombre' => $estructuraOrganizativa->getTipoEstructuraOrganizativa()->getNombre(),
                    ),
                    'parent_id' => (true === is_null($estructuraOrganizativa->getParent()) ? '#' : $estructuraOrganizativa->getId()),
                    'rol_id' => $this->getUser()->getRol()->getId(),
                ),
                'state' => array(
                    'opened' => true,
                    'selected' => false
                ),
                'li_attr' => array(
                    'title' => $estructuraOrganizativa->getTitle(),
                ),
                ' a_attr' => array(
                    'href' => '#',
                ),
                'children' => $this->getChildrens($estructuraOrganizativa)
            );

            $response = new Response(json_encode($arbol));
            $response->headers->set('Content-Type', 'application/json');

            return $response;
        } else
        {
            throw $this->createNotFoundException("Recurso no encontrado");
        }
    }

/**
 * Funcion auxiliar para obtener los hijos directos del nodo raiz 
 **/
private function getChildrens(EstructuraOrganizativa $estructuraOrganizativa)
    {
        $puedeContenerHijos = !in_array($estructuraOrganizativa->getTipoEstructuraOrganizativa()->getId(), array(6, 7, 8));
        if ($puedeContenerHijos === true)
        {
            $nodosParaJSON = array();
            $hijos = $this->getDoctrine()->getRepository('AppBundle:EstructuraOrganizativa')->children($estructuraOrganizativa, true);
            foreach ($hijos as $hijo)
            {
                $actualPuedeContenerHijos = !in_array($hijo->getTipoEstructuraOrganizativa()->getId(), array(6, 7, 8));
                if (in_array($hijo->getTipoEstructuraOrganizativa()->getId(), array(7, 8)))
                {
                    if (!$this->get('security.authorization_checker')->isGranted(array('ROLE_ESP_NAC', 'ROLE_ESP_PROV', 'ROLE_CONS_NAC', 'ROLE_CONS_PROV')))
                    {
                        continue;
                    }
                }
                $nodosParaJSON[] = array(
                    'id' => $hijo->getId(),
                    'text' => $hijo->getTitle(),
                    'icon' => \AppBundle\Utils\UI::getIconForTreeNode($hijo),
                    'data' => array(
                        'tipoEstructura' => array(
                            'id' => $hijo->getTipoEstructuraOrganizativa()->getId(),
                            'nombre' => $hijo->getTipoEstructuraOrganizativa()->getNombre(),
                        ),
                        'parent_id' => $hijo->getParent()->getId(),
                        'rol_id' => $this->getUser()->getRol()->getId(),
                    ),
                    'state' => array(
                        'opened' => false,
                        'selected' => false
                    ),
                    'li_attr' => array(
                        'title' => $hijo->getTitle(),
                    ),
                    ' a_attr' => array(
                        'href' => '#',
                    ),
                    'children' => $actualPuedeContenerHijos,
                );
            }

            return $nodosParaJSON;
        } else
        {
            return false;
        }
    }

public function renderArbolHijosAction(Request $request)
    {
        if ($request->isXmlHttpRequest())
        {
            $id = $request->get('id', 0);
            $estructura = $this->getDoctrine()->getRepository('AppBundle:EstructuraOrganizativa')->findOneJoinTipoEstructuraOrganizativa($id);
            if (is_null($estructura))
            {
                $vacio = array();
                $response = new Response(json_encode($vacio));
                $response->headers->set('Content-Type', 'application/json');

                return $response;
            }


            $hijosEstructura = $this->getDoctrine()->getRepository('AppBundle:EstructuraOrganizativa')->getChildren($estructura, true);
            $hijos = array();
            foreach ($hijosEstructura as $h)
            {
                if (in_array($h->getTipoEstructuraOrganizativa()->getId(), array(7, 8)))
                {
                    if (!$this->get('security.authorization_checker')->isGranted(array('ROLE_ESP_NAC', 'ROLE_ESP_PROV', 'ROLE_CONS_NAC', 'ROLE_CONS_PROV')))
                    {
                        continue;
                    }
                }

                $puedeContenerHijos = !in_array($h->getTipoEstructuraOrganizativa()->getId(), array(6, 7, 8));

                $hijos[] = array(
                    'id' => $h->getId(),
                    'text' => $h->getTitle(),
                    'icon' => \AppBundle\Utils\UI::getIconForTreeNode($h),
                    'data' => array(
                        'tipoEstructura' => array(
                            'id' => $h->getTipoEstructuraOrganizativa()->getId(),
                            'nombre' => $h->getTipoEstructuraOrganizativa()->getNombre(),
                        ),
                        'parent_id' => $h->getParent()->getId(),
                        'rol_id' => $this->getUser()->getRol()->getId(),
                    ),
                    'state' => array(
                        'opened' => false,
                        'selected' => false
                    ),
                    'li_attr' => array(
                        'title' => $h->getTitle(),
                    ),
                    ' a_attr' => array(
                        'href' => '#',
                    ),
                    'children' => $puedeContenerHijos,
                );
            }

            $response = new Response(json_encode($hijos));
            $response->headers->set('Content-Type', 'application/json');

            return $response;
        } else
        {
            throw $this->createNotFoundException("Recurso no encontrado.");
        }
    }

I should point out that the% EstructuraOrganizativa uses doctine extensions that make it easier for me to work with tree-like structures, and it even has functions to return all a nested html that is very likely jsTree can use, but the level of personalization that my tree of structures requires, as well as a series of data that I store for each node, does not help me that the Repository generates the html.

    
answered by 02.04.2017 / 15:19
source