jrowen / rhandsontable

A htmlwidgets implementation of Handsontable.js
http://jrowen.github.io/rhandsontable/
Other
383 stars 148 forks source link

Feature request: improve handling of display when initially hidden #366

Open pnacht opened 4 years ago

pnacht commented 4 years ago

I have this simple MWE, with a table hidden by a bsCollapsePanel:

library(shiny)
library(shinyBS)
library(rhandsontable)

ui <- function() {
  fluidPage(
    bsCollapsePanel(
      "Test",
      rHandsontableOutput("table")
    )
  )
}

server <- function(input, output, session) {
  output$table <- renderRHandsontable({
    rhandsontable(
      data.frame(
        a = 1:10,
        b = 2:11
      )
    )
  })
}

shinyApp(ui, server)

This works, in that we can open the panel and see the table. However, there is a noticeable "lag" between the panel expanding and the table suddenly appearing. This is unlike with other UI elements (i.e. textInput), where we can see the panel sweep down and gradually expose its contents.

This clearly seems to be a matter of the table only being generated once the panel opens, which takes a few moments too long.

After asking about this on SE, I got an answer on how to force the table UI to be created even when hidden:

server <- function(input, output, session) {
  output$table <- renderRHandsontable({
    rhandsontable(
      data.frame(
        a = 1:10,
        b = 2:11
      )
    )
  })

  outputOptions(output, "table", suspendWhenHidden = FALSE)
}

Indeed, now the table clearly exists when the panel expands, gradually exposing its rows. However, the table is drawn incorrectly, with only a single column (a) and no headers. If I then click on the table, the redraw is successful, showing the table correctly.

After panel expansion | after click image image

I then found a rendering workaround in a comment to #202, using htmlwidgets::onRender(), which I modified for this particular case:

server <- function(input, output, session) {
  output$table <- renderRHandsontable({
    rhandsontable(
      data.frame(
        a = 1:10,
        b = 2:11
      )
    ) %>%
    htmlwidgets::onRender("
      function(el, x) {
        var hot = this.hot
        $('#collapse').find('a').click(function() {
          setTimeout(function() {hot.render();}, 0);
        })
      }")
  })

  outputOptions(output, "table", suspendWhenHidden = FALSE)
}

With this, the behavior is perfect: the panel opens, gradually showing the table's contents in all their splendor. Worth noting that outputOptions is still necessary, otherwise we go back to square one, with the table popping in at the end.

However, as far as I can tell all this seems like it shouldn't be necessary.

Is it possible to fix this so that it "just works"? I'm going to use these workarounds for now, but they'll be a pain, having to call outputOptions() and htmlwidgets::onRender() for all my hidden tables (which are many!).