creativename
creativename

Reputation: 418

Passing reactive data to global environment

I want to use Shiny within RMarkdown for users to upload data (xlsx file).
Then I want to pass all the worksheets as R data frames (w/o reactivity) to run rest of the RMarkdown file.
I mainly want to convert them into data frames so I can use reticulate to run Python code as well.

I've tried this, and it doesn't seem to quite work:


library(dplyr)
library(miniUI)
library(shiny)
library(XLConnect)

launch_shiny <- function() {

  ui <- miniPage(
    gadgetTitleBar("Input Data"),
    miniContentPanel(
      fileInput(inputId = "my.file", label = NULL, multiple = FALSE)
    )
  )

  server <- function(input, output, session) {
    wb <- reactive({
      new.file <- input$my.file
      loadWorkbook(
        filename = new.file$datapath,
        create = FALSE,
        password = NULL
      )
    })

    observeEvent(input$done, {
      stopApp(c(wb()))
    })

  }
  runGadget(ui, server)

}

test <- launch_shiny()
df1 <- readWorksheet(object = test, sheet = "sheet1")
df2 <- readWorksheet(object = test, sheet = "sheet2")

It throws this error:

Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘readWorksheet’ for signature ‘"list", "character"’

I can return one sheet at a time using stopApp(readWorksheet(object = wb(), sheet = "sheet1")), but I can't seem to return an entire workbook or multiple data frames at the same time.

I don't really want to read in xlsx, save each sheet as csv in working directory, then read those files in again.
Would anyone have a good suggestion on how to get around this?

Upvotes: 0

Views: 802

Answers (1)

DS_UNI
DS_UNI

Reputation: 2650

The documentation of fileInput() states in the details:

datapath

The path to a temp file that contains the data that was uploaded. This file may be deleted if the user performs another upload operation.

Meaning that the datapath given in the input variable is a temporary file that is no longer accessible after you close the App, which is what the function readWorksheet will try to do.

So you'll have to read the sheets in the server and return the dataframes somehow. I did that by defining a second reactive value which is basically a list of dataframes returned by applying lapply on all the sheets in wb, in this case test will be this list of data frames.

There might be other ways (more efficient, or suits your purpose better) to do this, but here it is:

library(dplyr)
library(miniUI)
library(shiny)
library(XLConnect)

launch_shiny <- function() {

  ui <- miniPage(
    gadgetTitleBar("Input Data"),
    miniContentPanel(
      fileInput(inputId = "my.file", label = NULL, 
                multiple = FALSE)
    )
  )

  server <- function(input, output, session) {
    wb <- reactive({
      new.file <- input$my.file
      loadWorkbook(
        filename = new.file$datapath,
        create = FALSE,
        password = NULL
      )
    })

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    df_lst <- reactive({
      # read all sheets into a list
      lapply(getSheets(wb()), 
             function(sheet){
               readWorksheet(object = wb(), 
                             sheet = sheet)
             })
    })
   # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    observeEvent(input$done, {
      # get the list of dfs from the app
      stopApp(c(df_lst())) 
    })

  }
  runGadget(ui, server)
  }

test <- launch_shiny()

Upvotes: 1

Related Questions