tzema
tzema

Reputation: 461

R shiny: save data frames from multiple panels

In the following app, I would like to add a global button, to save the tables in the 2 panels at the same time. Ideally, they should be saved to an xlsx file, in tabs named after the corresponding tabs. Please note that the tabs were created using a module.

Many thanks!!

library(shiny)
library(DT)

modDtUi <- function(id){ # UI module
  ns = NS(id)
  DT::dataTableOutput(ns('x1'))
}


modDt <-  function(input, output, session, data, globalSession){ # Server module
  
  x <- data
  output$x1 <- DT::renderDataTable(x, selection = 'none', editable = TRUE)
  
  proxy <- dataTableProxy('x1', session = globalSession)
  

  
}



ui <- fluidPage(
  mainPanel(
    tabsetPanel(
    
      tabPanel("Table1", modDtUi("editable")),
      tabPanel("Table2", modDtUi("editable2"))
    )
  )
)


server <- function(input, output, session) {
  callModule(modDt,"editable", data = head(iris,10), globalSession = session)
  
  callModule(modDt,"editable2", data = tail(iris,5), globalSession = session)
}

shinyApp(ui = ui, server = server)


Upvotes: 1

Views: 90

Answers (1)

Ben
Ben

Reputation: 30474

I believe this demo works.

I used reactiveValues v$data to store the data inside the module. The module will return v$data so it can be retrieved when you want to save the data in the shiny server.

I also added an observeEvent to detect changes in the data, and update the data table with replaceData.

The excel file is created using the writexl library, but you could substitute with others of course.

Let me know if this works for you. I imagine there are some elements of this answer that can be improved upon - and if we can identify them, would like to edit further.

library(shiny)
library(DT)
library(writexl)

modDtUi <- function(id){ # UI module
  ns = NS(id)
  DT::dataTableOutput(ns(id))
}

modDt <-  function(input, output, session, data, id, globalSession){ # Server module
  
  v <- reactiveValues(data = data)
  
  output[[id]] <- DT::renderDataTable(v$data, selection = 'none', editable = TRUE)
  
  proxy <- dataTableProxy(id, session = globalSession)
  
  id_input = paste(id, "cell_edit", sep = "_")
  
  # Could add observeEvent here to detect edit event
  observeEvent(input[[id_input]], {
    info = input[[id_input]]
    if (!is.null(info)) {
      v$data[info$row, info$col] <<- DT::coerceValue(info$value, v$data[info$row, info$col])
    }
    replaceData(proxy, v$data, resetPaging = FALSE)
  })
  
  return(data = reactive({v$data}))
}

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      width = 2,
      actionButton("btn", "Save Both")
    ),
    mainPanel(
      tabsetPanel(
        tabPanel("Table1", modDtUi("editable1")),
        tabPanel("Table2", modDtUi("editable2"))
      )
    )
  )
)

server <- function(input, output, session) {
  
  e1 <- callModule(modDt, "editable1", data = head(iris,10), id = "editable1", globalSession = session)
  e2 <- callModule(modDt, "editable2", data = tail(iris,5), id = "editable2", globalSession = session)
  
  observeEvent(input$btn, {
    print("Saving...")
    sheets <- list("e1" = e1(), "e2" = e2())
    write_xlsx(sheets, "test.xlsx")
  })
  
}

shinyApp(ui = ui, server = server)

Upvotes: 2

Related Questions