2

I have a Shiny app with a Bookmark button and a DT table that allow the users to edit the contents (https://yuchenw.shinyapps.io/DT_Bookmark/). However, it seems like the Bookmark function cannot document the edited contents in the DT table.

Here is an example. I changed the car name in the first row to "Mazda RX4 aaaaa", and then I clicked "Bookmark button". It can generate an URL. But when I copied and pasted the URL to a new browser, it shows the original state of the app.

Is there a way to make the Bookmark function working? Here is the code.

library(shiny)
library(DT)

ui <- fluidPage(
  
  titlePanel("Bookmark DT Example"),
  
  sidebarLayout(
    
    sidebarPanel(
      bookmarkButton()
    ),
    
    mainPanel(
      DTOutput(outputId = "mDT")
    )
  )
)

server <- function(input, output){
  
  rev <- reactiveValues(dat = mtcars)

  output$mDT <- renderDT(
    mtcars,
    rownames = TRUE,
    selection = "none",
    editable = TRUE
  )

  dat_proxy <- dataTableProxy("mDT")

  observeEvent(input$mDT_cell_edit, {
    rev$dat <- editData(rev$dat, input$mDT_cell_edit, dat_proxy)
  })
  
}

shinyApp(ui, server, enableBookmarking = "url")
www
  • 38,575
  • 12
  • 48
  • 84

1 Answers1

4

The last modification to the datatable is registered in input$mDT_cell_edit.
input$mDT_cell_edit is saved in the bookmarked state, and you can use onRestore to restore it.

However, the full data used in the DT isn't saved : you could use onBookmark to save it too.
As this goes over the 2000 characters allowed by an url, you need to store the bookmark on the server with enableBookmarking = "server".

This is what is done in the code below, to show the way to move forward. It would of course be more efficient to save/restore the list of modifications only.

library(shiny)
library(DT)

server <- function(input, output){
  
  rev <- reactiveValues(dat = mtcars)
  
  output$mDT <- renderDT(
    rev$dat,
    rownames = TRUE,
    selection = "none",
    editable = TRUE
  )
  
  dat_proxy <- dataTableProxy("mDT")
  
  observeEvent(input$mDT_cell_edit, {
    info <- input$mDT_cell_edit
    i <- info$row 
    j <- info$col
    if (j>0)  {
      rev$dat[i, j] <<- DT::coerceValue(info$value, rev$dat[i, j])} 
    else {
      row.names(rev$dat)[i] <- info$value
      }
      
    DT::replaceData(dat_proxy, rev$dat, resetPaging = FALSE, rownames = T)
  })
  
  onBookmark(function(state) {
    state$values$rev_dat <- rev$dat
  })
  
  # restore table selection and search
  onRestored(function(state) {
    
    if (!identical(rev$dat,state$values$rev_dat)) {
      rev$dat <- state$values$rev_dat    
      DT::replaceData(dat_proxy, state$values$rev_dat, resetPaging = FALSE, rownames = T)
    }
  })
  
}

shinyApp(ui, server, enableBookmarking = "server")
Waldi
  • 39,242
  • 6
  • 30
  • 78
  • Thanks for your answer. When I tried your code on my computer, the app can initiate successfully, but when I edited one of the car names, it generates the following error: `Warning in DT::coerceValue(info$value, rev$dat[i, j]) : The data type is not supported: data.frame Warning: Error in [[: attempt to select less than one element in integerOneIndex 1: runApp` – www Jun 06 '21 at 02:33
  • It works on other columns, but rownames seem special : looking for a workaround – Waldi Jun 06 '21 at 06:16