Search code examples
rshinyshiny-reactivityrhandsontableshinystore

How to implement shinyStore when using a table generated by the rhandsontable R package?


I'm trying to implement the "save" feature from the answer in post How to use the localStorage option for DT in R Shiny? into my table rendered with rhandsontable but it's not working. That post involves table package DT, whereas I'm using rhandsontable and need to stick with rhandsontable. By "save", I mean preserving the table with all its cumulative inputs/outputs from one session to the next, which the referred-to post does for DT table. I will need to implement the "clear" function from that post later, but first I want to see how "save" works, and what I'm doing wrong in my below attempt, before moving on to adapting the "clear" function.

Below code has comments # add... for functions I pulled in from the reference post.

How would I enable the save feature in this rhandsontable example?

I get the following error message: Error : Can't access reactive value 'hottable' outside of reactive consumer. Do you need to wrap inside reactive() or observer()?

Code:

# If not installed already, un-comment and run the below 3 lines to install shinyStore package:
# install.packages("devtools")
# library(devtools)
# install_github("trestletech/shinyStore")

library(rhandsontable)
library(shiny)
library(shinyStore)

myDF <- data.frame(x = c(1, 2, 3))

ui <- fluidPage(
  initStore("store", "shinyStore-ex1"), # add
  br(),
  fluidRow(
    column(6,
           actionButton('addCol','Add column'),
           actionButton("save", "Save", icon("save")), # add
           actionButton("clear", "Clear", icon("stop")) # add
    )
  ),
  br(),rHandsontableOutput('hottable')
)

server <- function(input, output, session) {
  EmptyTbl <- reactiveVal(myDF)
  
  rv <- reactiveValues(uiTable = hot_to_r(input$hottable)) # add
  
  observeEvent(input$hottable, {
    EmptyTbl(hot_to_r(input$hottable))
  })
  
  output$hottable <- renderRHandsontable({
    rhandsontable(EmptyTbl(),useTypes = FALSE)
  })
  
  observeEvent(input$addCol, {
    newCol <- data.frame(c(1, 2, 3))
    names(newCol) <- paste("Col", ncol(hot_to_r(input$hottable)) + 1)
    EmptyTbl(cbind(EmptyTbl(), newCol))
  })
  
  # add observeEvent() below:
  observeEvent(input$save,{
    updateStore(session,name = "uiTable",rv$uiTable)
  },ignoreInit = TRUE)
  
}

shinyApp(ui, server)

Solution

  • Please check the following:

    # If not installed already, un-comment and run the below 3 lines to install shinyStore package:
    # install.packages("devtools")
    # library(devtools)
    # install_github("trestletech/shinyStore")
    
    library(rhandsontable)
    library(shiny)
    library(shinyStore)
    
    myDF <- data.frame(x = c(1, 2, 3))
    
    ui <- fluidPage(
      initStore("store", "shinyStore-ex1"),
      br(),
      fluidRow(column(
        6,
        actionButton('addCol', 'Add column'),
        actionButton("save", "Save", icon("save")),
        actionButton("clear", "Clear", icon("stop")) # add
      )),
      br(),
      rHandsontableOutput('hottable')
    )
    
    server <- function(input, output, session) {
      uiTable <- reactiveVal(myDF)
      
      output$hottable <- renderRHandsontable({
        rhandsontable(uiTable(), useTypes = FALSE)
      })
    
      observeEvent(input$hottable, {
        uiTable(hot_to_r(input$hottable))
      })
      
      observeEvent(input$addCol, {
        newCol <- data.frame(c(1, 2, 3))
        names(newCol) <-
          paste("Col", ncol(hot_to_r(input$hottable)) + 1)
        uiTable(cbind(uiTable(), newCol))
      })
      
      observeEvent(input$save, {
        updateStore(session, name = "uiTable", uiTable())
      }, ignoreInit = TRUE)
      
      observeEvent(input$clear, {
        # clear tracking table:
        uiTable(myDF)
        
        # clear shinyStore:
        updateStore(session, name = "uiTable", myDF)
      }, ignoreInit = TRUE)
      
      observeEvent(input$store$uiTable, {
        uiTable(as.data.frame(input$store$uiTable))
      })
    }
    
    shinyApp(ui, server)
    

    PS: As an alternative approach we could use rhandsontable::set_data() in the same manner as DT::replaceData to avoid recreating the widget via renderRHandsontable if the underlying table was changed.