rhandsontable icon indicating copy to clipboard operation
rhandsontable copied to clipboard

Changing an RHOT table's dimensions loses all input and triggers hot_to_r()

Open D3SL opened this issue 4 years ago • 2 comments

In order to handle different resolutions I used javascript to get viewport dimensions and passed that as an option to rhandsontable() as height=(input$dimension[1]*.45). However while using this anything that causes RHOT to resize itself appears to cause a re-render that drops all user input. This triggers hot_to_r() as well.

D3SL avatar Nov 15 '20 08:11 D3SL

Hi @D3SL I think I've had a lot of the same problems as yourself with dynamically changing the height of a rhandsontable. I wondered if you'd made any decent headway? I have managed to solve the dropping of all user changes to the table issue using a server side reactive variable. however the whole handsontable is re rendered whenever the window is resized.

Below is my minimal example of a resizing hot where user changes persist

library(shiny)
library(shinymaterial)
library(tibble)
library(magrittr)
library(rhandsontable)

#custom js to add input$dimension to shiny that updates on window resize
custom_js_table_height <- '
                                var dimension = [0, 0];
                                $(document).on("shiny:connected", function(e) {
                                    dimension[0] = window.innerWidth;
                                    dimension[1] = window.innerHeight;
                                    Shiny.onInputChange("dimension", dimension);
                                });
                                $(window).resize(function(e) {
                                    dimension[0] = window.innerWidth;
                                    dimension[1] = window.innerHeight;
                                    Shiny.onInputChange("dimension", dimension);
                                });
                            '


ui <-
  material_page(
    title = "A long rhnadsontable with window resizing",
    
    #add custom js
    tags$head(tags$script(HTML(
      custom_js_table_height
    ))),
    material_card(
      title = "Here is my table",
      
      rhandsontable::rHandsontableOutput("iris", height = "100%", width = "100%"),
      
      h6("And here is a ui element after")
    )
  )




server <- function(input, output) {
  #define reactive values, one for server side data storage, one for the
  #server side height of the hands on table
  reactive_iris <- reactiveValues(data = iris)
  reactive_hot_height <- reactiveValues(height = 100)
  
  #whenever the table changes, check if identical to server side, if not
  #update server side
  observeEvent(input$iris, {
    current_table <- input$iris %>% hot_to_r() %>% as_tibble()
    old_stored_table <- reactive_iris[["data"]] %>% as_tibble()
    
    if (!identical(current_table, old_stored_table)) {
      cat("updated server side df\n")
      reactive_iris[["data"]] <- current_table
    }
    
  })
  
  #when window dimension changes, update server side height.
  #One downside is having to estimate the number of pixels to remove from the
  #total window height, this would need updating any time the ui updated
  observeEvent(input$dimension, {
    reactive_hot_height[["height"]] <- input$dimension[2] - 230
  })
  
  
  #render the table, table is rerendered every time reactive_iris or
  #reactive_hot_height changes
  #
  #additional rows added
  output$iris <- ({
    renderRHandsontable({
      cat("table rendered\n")
      rhandsontable(reactive_iris[["data"]],
                    rowHeaders = TRUE,
                    height = reactive_hot_height[["height"]],
      ) %>%
        hot_table(stretchH = "all")
    })
    
  })
  
  
}


shinyApp(ui = ui, server = server)

Mikea0228 avatar Mar 12 '21 13:03 Mikea0228

Hi @D3SL I think I've had a lot of the same problems as yourself with dynamically changing the height of a rhandsontable. I wondered if you'd made any decent headway? I have managed to solve the dropping of all user changes to the table issue using a server side reactive variable. however the whole handsontable is re rendered whenever the window is resized.

To be honest I just left it with a generically 1080p friendly size and forgot about this. As near as I could tell the problem was that upon re-rendering the RHOT with its new size it showed up blank, which was registered as if it were a user input, and the new blank table was saved overwriting all of the user's actual input.

If I'm reading your code right what you do is save the initial state of the table to old_stored_table when the app is first loaded and anytime a new input is detected you compare current_rhot to old_stored_table, updating old_stored_table if the two aren't the same.

If your method works it means I'm wrong about why resizing the table is wiping out all saved data from user inputs, because as soon as the user inputs data which is saved to old_stored_table a new blank table would pass the !identical() test you use and still overwrite old_stored_table.

D3SL avatar Nov 28 '21 05:11 D3SL