1

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)

2 Answers2

2

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.

ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
  • I'm going to print out your solution and my OP code, and compare them line by line to bolster my understanding, of where I went wrong. THANK YOU AGAIN! You are a treasure for the community. – Curious Jorge - user9788072 Nov 03 '22 at 16:41
  • @CuriousJorge-user9788072 - Thanks for your kind words and sorry for the missing explanation - I was in a hurry. Let me know if you need details. Cheers – ismirsehregal Nov 03 '22 at 17:35
0

Please defer to ismirsehregal's complete solution. The below addresses my mistakes in the OP code, starting with a working solution that doesn't involve the "clear" function and preserving some OP object names for comparison ease, and then a line-by-line comparison of the code highlighting my OP errors and explaining the corrections:

Working code:

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)
  
  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))
  })
  
  observeEvent(input$save,{
    updateStore(session,name = "EmptyTbl",EmptyTbl())
  },ignoreInit = TRUE)
  
  observeEvent(input$store$EmptyTbl,{
    EmptyTbl(as.data.frame(input$store$EmptyTbl))
  })
  
}

shinyApp(ui, server)

Errors highlighted and explained: enter image description here