stla / jsTreeR

A wrapper of the jQuery plugin `jsTree`.
Other
45 stars 3 forks source link

changed.jstree event is not captured #34

Open stewerner opened 1 month ago

stewerner commented 1 month ago

I was trying to implement a button to select / deselect all nodes. However, it seems this change is not reflected in the shiny input:

screen

Example:

library(jsTreeR)
library(shiny)
library(shinyjs)
library(jsonlite)

nodes <- list(
  list(
    text = "Branch 1",
    state = list(
      opened = TRUE,
      disabled = FALSE
    ),
    type = "parent",
    children = list(
      list(
        text = "Leaf A",
        state = list(
          opened = TRUE,
          disabled = FALSE,
          selected = FALSE
        ),
        type = "child"
      ),
      list(
        text = "Leaf B",
        state = list(
          opened = TRUE,
          disabled = FALSE,
          selected = FALSE
        ),
        type = "child"
      ),
      list(
        text = "Leaf C",
        state = list(
          opened = TRUE,
          disabled = FALSE,
          selected = FALSE
        ),
        type = "child"
      ),
      list(
        text = "Leaf D",
        state = list(
          opened = TRUE,
          disabled = FALSE,
          selected = FALSE
        ),
        type = "child"
      )
    )
  ),
  list(
    text = "Branch 2",
    type = "parent",
    state = list(
      opened = TRUE,
      disabled = FALSE,
      selected = FALSE
    )
  )
)

ui <- fluidPage(
  useShinyjs(),
  jstreeOutput("mytree"), 
  actionButton("select_all_nodes", "Select all"),
  actionButton("deselect_all_nodes", "Deselect all"),
  verbatimTextOutput("mytree_full")
)

server <- function(input, output, session) {
  output[["mytree"]] <- renderJstree({
    jstree(nodes, contextMenu = TRUE, checkboxes = TRUE)
  })

  observeEvent(input$select_all_nodes, {
    runjs("$('#mytree').jstree('select_all', false);")
  })

  observeEvent(input$deselect_all_nodes, {
    runjs("$('#mytree').jstree('deselect_all', false);")
  })

  output$mytree_full <- renderPrint({toJSON(input$mytree_full, pretty = TRUE)})
}

shinyApp(ui, server)
stla commented 1 month ago

Hello,

Thank you for your message. I've just checked my JavaScript code and then I've seen that I commented out one line in the handler of the changed.jstree event so that input$mytree_full is not updated. Unfortunately I don't remember why I commented out this line.

ismirsehregal commented 1 month ago

Hi, that's strange. The changed event seems to be pretty straight forward. How are the manual selections captured?

stla commented 1 month ago

There's nothing strange.

A selection triggers the select_node.jstree event. And in inst/htmlwidgets/jstreer.js I have a handler for this event:

        $el.on("select_node.jstree", function(e, data) {
          if(inShiny) {
            setShinyValue(data.instance, checkboxes);
          } // modif 9/10/2023
        });

My function setShinyValue updates input$mytree_full.

But as I said, I commented out the call to this function in my handler of the changed.jstree event:

        $el.on("changed.jstree", function(e, data) {
          if(inShiny) {
            //            Shiny.setInputValue(
            //              id, getNodesWithChildren(data.instance.get_json())
            //            );
            setShinyValueSelectedNodes(data.instance, leavesOnly, checkboxes);
            if(checkboxes) {
              setShinyValueCheckedNodes(data.instance, leavesOnly);
            }
            //setShinyValue(data.new_instance); // modif 9/10/2023
          }
        });

I don't remember why. Perhaps because this function was called multiple times in some circumstances.

ismirsehregal commented 1 month ago

Strange, as I would have expected select_all to trigger a more general event like select_node. And it seems changed.jstree should be the way to go:

https://groups.google.com/g/jstree/c/3LLvol8nx1A/m/xvoTIonFticJ

However, select_all also triggers select_all.jstree. Same logic for deselect. Maybe this would be an option.

ismirsehregal commented 1 month ago

It seems changed.jstree was commented out after I filed this issue which you fixed with this commit.

Maybe it would have been sufficient to just add the after_open and after_close events?

ismirsehregal commented 1 month ago

I just left a PR which seems to fix this.

The original line //setShinyValue(data.new_instance); // modif 9/10/2023

didn't work but

setShinyValue(data.instance);

did.

(an alternative still would be listening on select_all.jstree / deselect_all.jstree)