Export all user inputs in a Shiny app to file and load them later

前端 未结 3 1719
臣服心动
臣服心动 2020-12-15 13:39

My Shiny app has several inputs which are used to define several parameters of a generated plot. It\'s very likely that the user will spend some minutes going through all po

相关标签:
3条回答
  • 2020-12-15 14:22

    Unless you're doing a lot of highly flexible type inputs (renderUI blocks which could be any sort of input) then you could create a list storing all current values, use dput to save them to a file with a corresponding dget to read it in.

    In one app I have, I allow users to download a file storing all their uploaded data plus all their options.

    output$saveData <- downloadHandler(
      filename = function() { 
        paste0('Export_',Sys.Date(),'.sprout')
      },
      content = function(file) {
        dataToExport = list()
        #User specified options
        dataToExport$sproutData$transformations=sproutData$transformations  #user specified transformations
        dataToExport$sproutData$processing=sproutData$processing  #user specified text processing rules
        dataToExport$sproutData$sc=sproutData$sc  #user specified option to spell check
        dataToExport$sproutData$scOptions=sproutData$scOptions  #user specified spell check options (only used if spell check is turned on)
        dataToExport$sproutData$scLength=sproutData$scLength  #user specified min word lenght for spell check (only used if spell check is turned on)
        dataToExport$sproutData$stopwords=sproutData$stopwords  #user specified stopwords
        dataToExport$sproutData$stopwordsLastChoice=sproutData$stopwordsLastChoice #last pre-built list selected
        dput(dataToExport,file=file)
      }
    )
    

    Here I make an empty list, then I stick in the values I use in my app. The reason for the dTE$sD$name structure is that I have a reactiveValues called sproutData which stores all user selected options and data. So, I preserve the structure in the output.

    Then, I have a load data page which does the following:

    output$loadStatusIndicator = renderUI({
      worked = T
      a = tryCatch(dget(input$loadSavedData$datapath),error=function(x){worked<<-F})
      if(worked){
        #User specified options
        a$sproutData$transformations->sproutData$transformations  #user specified transformations
        a$sproutData$processing->sproutData$processing  #user specified text processing rules
        updateCheckboxGroupInput(session,"processingOptions",selected=sproutData$processing)
        a$sproutData$sc->sproutData$sc  #user specified option to spell check
        updateCheckboxInput(session,"spellCheck",value = sproutData$sc)
        a$sproutData$scOptions->sproutData$scOptions  #user specified spell check options (only used if spell check is turned on)
        updateCheckboxGroupInput(session,"spellCheckOptions",selected=sproutData$scOptions)
        a$sproutData$scLength->sproutData$scLength  #user specified min word lenght for spell check (only used if spell check is turned on)
        updateNumericInput(session,"spellCheckMinLength",value=sproutData$scLength)
        a$sproutData$stopwords->sproutData$stopwords  #user specified stopwords
        a$sproutData$stopwordsLastChoice->sproutData$stopwordsLastChoice
        if(sproutData$stopwordsLastChoice[1] == ""){
          updateSelectInput(session,"stopwordsChoice",selected="none")
        } else if(all(sproutData$stopwordsLastChoice == stopwords('en'))){
          updateSelectInput(session,"stopwordsChoice",selected="en")
        } else if(all(sproutData$stopwordsLastChoice == stopwords('SMART'))){
          updateSelectInput(session,"stopwordsChoice",selected="SMART")
        }
        HTML("<strong>Loaded data!</strong>")
      } else if (!is.null(input$loadSavedData$datapath)) {
        HTML(paste("<strong>Not a valid save file</strong>"))
      }
    })
    

    The actual output is a table which details what it found and what it set. But, because I know all the inputs and they don't change, I can explicitly store them (default or changed value) and then explicitly update them when the save file is uploaded.

    0 讨论(0)
  • 2020-12-15 14:24

    If you look at the code of the shiny input update functions, they end by session$sendInputMessage(inputId, message). message is a list of attributes that need to be changed in the input, for ex, for a checkbox input: message <- dropNulls(list(label = label, value = value))

    Since most of the input have the value attribute, you can just use the session$sendInputMessage function directly on all of them without the try.

    Here's an example, I created dummy_data to update all the inputs when you click on the button, the structure should be similar to what you export:

    ui.R

    library(shiny)
    shinyUI(fluidPage(
      textInput("control_label",
                "This controls some of the labels:",
                "LABEL TEXT"),
      numericInput("inNumber", "Number input:",
                   min = 1, max = 20, value = 5, step = 0.5),
      radioButtons("inRadio", "Radio buttons:",
                   c("label 1" = "option1",
                     "label 2" = "option2",
                     "label 3" = "option3")),
      actionButton("update_data", "Update")
    
      ))
    

    server.R

    library(shiny)
    
    dummy_data <- c("inRadio=option2","inNumber=10","control_label=Updated TEXT" )
    
    shinyServer(function(input, output,session) {
      observeEvent(input$update_data,{    
        out <- lapply(dummy_data, function(l) unlist(strsplit(l, "="))) 
       for (inpt in out) {
         session$sendInputMessage(inpt[1], list(value=inpt[2]))
        }
       })
    
    })
    

    All the update functions also preformat the value before calling session$sendInputMessage. I haven't tried all possible inputs but at least for these 3 you can pass a string to the function to change the numericInput and it still works fine.

    If this is an issue for some of your inputs, you might want to save reactiveValuesToList(input) using save, and when you want to update your inputs, use load and run the list in the for loop (you'll have to adapt it to a named list).

    0 讨论(0)
  • 2020-12-15 14:24

    This is a bit old but I think is usefull to post a complete example, saving and loading user inputs.

    library(shiny)  
    
    ui <- shinyUI(fluidPage(
      textInput("control_label",
                "This controls some of the labels:",
                "LABEL TEXT"),
      numericInput("inNumber", "Number input:",
                   min = 1, max = 20, value = 5, step = 0.5),
      radioButtons("inRadio", "Radio buttons:",
                   c("label 1" = "option1",
                     "label 2" = "option2",
                     "label 3" = "option3")),
    
      actionButton("load_inputs", "Load inputs"), 
      actionButton('save_inputs', 'Save inputs')
    
    )) 
    
    server <-  shinyServer(function(input, output,session) { 
    
      observeEvent(input$load_inputs,{   
    
        if(!file.exists('inputs.RDS')) {return(NULL)}
    
        savedInputs <- readRDS('inputs.RDS')
    
        inputIDs      <- names(savedInputs) 
        inputvalues   <- unlist(savedInputs) 
        for (i in 1:length(savedInputs)) { 
          session$sendInputMessage(inputIDs[i],  list(value=inputvalues[[i]]) )
        }
      })
    
      observeEvent(input$save_inputs,{ 
        saveRDS( reactiveValuesToList(input) , file = 'inputs.RDS')
      })  
    })
    
    0 讨论(0)
提交回复
热议问题