javascriptrshinyhtmlwidgetsjstreer

Error in init.ts when using jsTreeR's grid parameter


I've already raised the issue here and here. By now a workaround exists however, I still don't understand the root cause.

When running the following app using the latest CRAN version of library(shiny) and library(jsTreeR) (2.5.0) after resizing the browser window this error can be seen in the browser console:

image

Example app:

library(jsTreeR)
library(shiny)

nodes <- list(
  list(
    text = "Fruits",
    type = "fruit",
    children = list(
      list(
        text = "Apple",
        type = "fruit",
        data = list(
          quantity = 20
        )
      ),
      list(
        text = "Banana",
        type = "fruit",
        data = list(
          quantity = 31
        )
      ),
      list(
        text = "Grapes",
        type = "fruit",
        data = list(
          quantity = 34
        )
      )
    ),
    state = list(
      opened = TRUE
    )
  )
)

grid <- list(
  columns = list(
    list(
      width = 200,
      header = "Product"
    ),
    list(
      width = 150,
      value = "quantity",
      header = "Quantity"
    )
  )
)

ui <-   fluidPage(
  titlePanel("jsTree grid"),
  jstreeOutput("jstree")
)

server <-   function(input, output, session){
  output$jstree <- renderJstree(jstree(nodes, search = TRUE, grid = grid))
}

shinyApp(ui, server)

This is only the case when jstree is provided with the grid parameter.

@StéphaneLaurent identified that the class shiny-bound-output is assigned to the jstree-grid-wrapper div which causes the issue and that removing it prevents the error.

I'd like to understand why this class is added in the first place and if there is a proper way to implement the grid parameter without running into this issue.


Solution

  • I think the answer is in the file jstreegrid.js, in the listener of the ready.jstree event, code starting at line 493:

    .on(
      "ready.jstree",
      $.proxy(function (e, data) {
        // find the line-height of the first known node
        var anchorHeight = this.element
            .find("[class~='jstree-anchor']:first")
            .outerHeight(),
          q,
          cls = this.element.attr("class") || "";
        $(
          '<style type="text/css">div.jstree-grid-cell-root-' +
            this.rootid +
            " {line-height: " +
            anchorHeight +
            "px; height: " +
            anchorHeight +
            "px;}</style>"
        ).appendTo("head");
    
        // add container classes to the wrapper - EXCEPT those that are added by jstree, i.e. "jstree" and "jstree-*"
        q = cls.split(/\s+/).map(function (i) {
          var match = i.match(/^jstree(-|$)/);
          return match ? "" : i;
        });
        this.gridWrapper.addClass(q.join(" "));
      }, this)
    );
    

    In this code, cls = this.element.attr("class") is the character string made of all classes attributed to the tree, separated by a white space. Then q = cls.split(/\s+/).map(... is the array of all these classes except the jstree class and the classes started with jstree-, and these classes are then added to the grid (the jstree-grid-wrapper div). And then I see two possibilities:


    Edit

    I've just checked, and the tree has both classes shiny-bound-output and jstreer at this stage.

    A possible solution is to modify jstreegrid.js so that it does not copy these classes to the grid. In fact I don't see any other solution.