Export all user inputs to the Shiny app and download them later.

My Shiny application has several inputs that are used to determine several parameters of the generated chart. It is very likely that the user will spend several minutes going through all the possible options until he is satisfied with the result. Obviously, the plot can be exported in different formats, but it is possible that the user will want to recreate the same plot with different data later, or maybe just change one small detail.

Because of this, I need to offer the user a way to export all of his settings and save this file for later use. I developed an approach, but it does not work well. I use reactiveValuesToList to get the names of all input elements and save as a simple text file with the format inputname=inputvalue . This is the downloadHandler on server.R :

 output$bt_export <- downloadHandler( filename = function() { "export.txt" }, content = function(file) { inputsList <- names(reactiveValuesToList(input)) exportVars <- paste0(inputsList, "=", sapply(inputsList, function(inpt) input[[inpt]])) write(exportVars, file) }) 

This works fine, but the download is not going very smoothly. Since I did not (and could not figure out how) to save the input type, I have to update the values ​​blindly. Here is how I do it:

 importFile <- reactive({ inFile <- input$fileImport if (is.null(inFile)) return(NULL) lines <- readLines(inFile$datapath) out <- lapply(lines, function(l) unlist(strsplit(l, "="))) return(out) }) observe({ imp <- importFile() for (inpt in imp) { if (substr(inpt[2], 0, 1) == "#") { shinyjs::updateColourInput(session, inputId = inpt[1], value = inpt[2]) } else { try({ updateTextInput(session, inputId = inpt[1], value = inpt[2]) updateNumericInput(session, inputId = inpt[1], value = inpt[2]) updateSelectInput(session, inputId = inpt[1], selected = inpt[2]) }) } } }) 

Besides the shinyjs::colorInput , which can be recognized by the beginning of # , I have to use try() for the rest. This works partially, but some inputs are not updated. Checking the exported file manually shows that there is input that has not been updated, so I believe updating 100+ entries right away is not a good idea. Also the try() does not look very good and probably not a good idea.

The application is close to completion, but is likely to be updated in the future by adding / modifying some inputs. This is acceptable if it even makes some of the "old" exported inputs invalid, as I try to maintain backward compatibility. But I'm looking for an approach that doesn't just write hundreds of lines to update inputs one by one.

I was thinking about using save.image() , but just using load() does not restore application inputs. I also looked at a way to somehow update all the inputs at once, and not one by one, but came up with nothing. Is there a better way to export all user inputs to a file and then load them all? It does not matter if it improves this work, or if a completely different approach works.

+5
source share
3 answers

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

Since most of the input has a value attribute, you can simply use the session$sendInputMessage directly for all of them without try .

Here is an example, I created dummy_data to update all inputs when I click on the button, the structure should look like 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 update functions also pre-format the value before calling session$sendInputMessage . I have not tried all possible inputs, but at least for these 3 you can pass a string to the function to change numericInput , and it still works fine.

If this is a problem for some of your inputs, you can save reactiveValuesToList(input) with save , and when you want to update your inputs, use load and run the list in a for loop (you have to adapt it to the named list).

+9
source

This is a bit outdated, but I find it useful 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') }) }) 
+3
source

If you do not make many flexible input types ( renderUI blocks, which can be any inputs), you can create a list that stores all current values, use dput to save them in a file with the corresponding dget to read it.

In one application I have, I allow users to upload a file that saves all their downloaded data, as well as 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 to the values ​​that I use in my application. The reason for the dTE$sD$name structure is that I have a reactiveValues called sproutData , which stores all the user-selected parameters and data. So, I keep the structure in the output.

Then I have a load data page that 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 conclusion is a table that details what she discovered and what she installed. But, since I know all the inputs, and they do not change, I can explicitly store them (the default value or the changed value), and then explicitly update them when loading the save file.

+1
source

All Articles