jrowen / rhandsontable

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

Bug when running rhandsontable in modal dialogue (reference issue # 24) #387

Open lgirola opened 3 years ago

lgirola commented 3 years ago

Hi, rhandsontable is very useful for the types of models I work with. And in combination with modal dialogue, it makes the model input process very user friendly.

However, when an rhandsontable is rendered in a modal box, depending on the circumstance only a part of the table is rendered until the user clicks on the partially rendered table. Is this a bug, or am I using rhandsontable incorrectly? Please see super simple MWE code below. It renders the initial table just fine, but when the user makes a change to the table (for example inserting a row with data) and tries re-rendering it by clicking the "Show" action button, only part of the table is rendered until the user clicks on the table.

The first image shows the rendered table when first invoking the app - looks good. The second image shows the rendered table after having inserted a row/data and then clicking "Show" -- I only get a partial rendering of the table until clicking on it, and then the complete table pops up (as it should have done after the "Show" click). The third images shows the completely and correctly rendered table after inserting a 3rd row, after clicking "Show", and after clicking on the partially rendered table.

library(shiny)
library(rhandsontable)

ui <- fluidPage(actionButton("show", "show"), 
                actionButton("change", "Change"))

server <- function(input, output, session) {
  dat <- reactiveVal(data.frame(x = runif(2), 
                                y = runif(2)))

  dat1 <- reactive({
    if(is.null(input$hot)){
      dat()
    } else {
      as.data.frame(hot_to_r(input$hot))
    }
  })

  observeEvent(input$show, {
    showModal(modalDialog(rHandsontableOutput("hot")))
  })

  observeEvent(input$change, 
               dat(data.frame(x = runif(2), y = runif(2))))

  output$hot <- renderRHandsontable(rhandsontable(dat1()))

}

shinyApp(ui,server)

Image1 Image2 Image3

ismirsehregal commented 3 years ago

Link to related SO-Question: https://stackoverflow.com/questions/68496116/in-r-shiny-how-do-you-reset-data-reversing-all-manual-inputs-in-rhandsontable/

timelyportfolio commented 3 years ago

@ismirsehregal Since the handsontable is rendered into a hidden element, handsontable will not know a size. My solution below makes two changes:

  1. Add some JavaScript to re-render the handsontable on the show.bs.modal event
  2. Change to height: auto; in rHandsontableOutput
library(shiny)
library(rhandsontable)

ui <- fluidPage(
  tags$head(tags$script(HTML(
"
$(document).on('show.bs.modal', function() {
  var w = HTMLWidgets.find('#hot');
  var hot = w && w.hot ? w.hot : null
  if(hot === null) {return null} // if handsontable not defined or rendered then exit

  hot.render();
})
"
  ))),
  actionButton("show", "show"), 
  actionButton("change", "Change")
)

server <- function(input, output, session) {
  dat <- reactiveVal(data.frame(x = runif(2), 
                                y = runif(2)))

  dat1 <- reactive({
    if(is.null(input$hot)){
      dat()
    } else {
      as.data.frame(hot_to_r(input$hot))
    }
  })

  observeEvent(input$show, {
    showModal(
      modalDialog(
        {
          # messy way to change height to auto from 100%
          hot <- rHandsontableOutput("hot")
          hot[[1]]$attribs$style <- "height: auto; width: 100%;"
          hot
        }
      )
    )
  })

  observeEvent(input$change, 
               dat(data.frame(x = runif(2), y = runif(2))))

  output$hot <- renderRHandsontable(rhandsontable(dat1(), licenseKey = "non-commercial-and-evaluation"))

}

shinyApp(ui,server)
DillonHammill commented 3 years ago

Thanks a lot for looking into this @timelyportfolio! Perhaps @jrowen can offer advice on making a more permanent solution, but this works well in my testing.

lgirola commented 3 years ago

The above post from timelyportfolio does clean up the re-rendering issue noted in the original #387 Issue posting dated Jul-24. However, the "Change" (or "Reset") button doesn't work -- clicking it should revert the output back to a 2 x 2 table of 4 random numbers obviating any manual changes that have been made to the table. Any ideas on how to fix this? As I've dealt with this issue and tried alternative solutions, resolving the re-rendering issue triggers the reset issue, and resolving the reset issue triggers the re-rendering issue. There needs to be a way out of this circle.