Make conditionalPanel depend on files uploaded with fileInput - r

So I'm trying to make a shiny app where I have a button which only shows up if files have been uploaded; for this im using conditionalPanel.
ui.R:
require(shiny)
shinyUI(pageWithSidebar(
headerPanel("My App"),
sidebarPanel(
fileInput("files", "Choose file"),
conditionalPanel(
condition = "input.files",
actionButton("submitFiles", "Submit files for processing"))),
mainPanel(h3("Nothing to see here"))
))
I don't think there's anything to care about in my server.R, since the above example doesn't do anything. With the above condition, the button never shows up, i.e. the condition is never true.
Some things I've tried for my condition are input.files.length > 0, input.files.size() > 0, both of which result in the button being present before I upload a file. I'm guessing this is because input$files is an empty data.frame before choosing files, and so has a non-zero length/size, is that right?
What condition can I use to hide the button until at least one file is done uploading?
I think another option would be to replace conditionalPanel with uiOutput, and call renderUI({actionButton(...)}) inside of an observe/isolate block in server.R which is watching input.files (if (nrow(input$files) < 1) return()); is that the only way? If I can do this either way, what would make me pick one or the other (beyond conditionalPanel resulting in less code)?

You have to make a reactive output returning the status of the uploading and set the option suspendWhenHidden of this output to FALSE.
More precisely, in server.R you surely have a reactive function, say getData() to make a dataframe from the uploaded file. Then do this:
getData <- reactive({
if(is.null(input$files)) return(NULL)
......
})
output$fileUploaded <- reactive({
return(!is.null(getData()))
})
outputOptions(output, 'fileUploaded', suspendWhenHidden=FALSE)
And in ui.R you can use conditionalPanel() by doing:
conditionalPanel("output.fileUploaded",
......

Related

In Shiny, the submit button isn't running

I wrote a R script (MAIN.R) that converts PDF tables to CSV. When I run MAIN.R as an individual file, it functions well. I've tried it many times.
Currently, I'm working on a R shiny app that uses "MAIN.R" as a source and takes a pdf file as input. When I push the submit button, the output should appear in the MAIN panel. Unfortunately, the submit button does not function as intended.
May anyone please assist me with this, as I am new to Shiny?
UI.R
shinyUI(fluidPage(
titlePanel("DATASET CONVERSION"),
sidebarLayout(
fileInput("filein", label = h2("Select a file to convert.")),
submitButton("Submit")
),
mainPanel(
tableOutput("Dataset")
)
)
)
Server.R
source("MAIN.R")
shinyServer(function(input, output) {
outputdf <- reactive({ input$filein
})
output$Dataset <- renderTable({
outputdf()
})
})
Your Submit button is not currently linked to anything, so it will not do anything. If I am reading the code right, you are just taking the input dataset and storing it as the output of outputdf. Your output$Dataset then just picks up that outputdf and displays it as-is, without any work being done on it.
You use an action button like so:
## In UI.R
actionButton("execute", "Execute the Main Function")
## In Server.R
observeEvent(input$execute, {
## Do stuff here
})
Note that the actionButton has two parameters, inputID (which is how you refer to it) and text to display on top. For example, with input$filein, 'filein' is the inputID.
In Server.R, observeEvent won't do anything until it detects a change in input$execute, which happens when someone clicks the button. That is where you put your code to do stuff.
Now, in output$Dataset, you need to access the results of whatever you did in that observeEvent. One way to do that is to use a reactiveValue. This is just like a reactive, but instead of a function, it stores a data element. Initialize it as an empty dataframe, and then update it in the observeEvent. Something like this:
## In Server.R
treated_output <- reactiveValue(data.frame())
observeEvent(input$execute, {
## Run the function on the file
updated <- main_function(input$filein)
# Update your reactiveValue
treated_output(updated)
})
output$Dataset <- renderTable({
treated_output()
})
Does this make sense?

How can I store and retrieve files uploaded with fileInput after resetting it Shiny

I would like to create a storage to store and retrieve uploaded files even after fileInput is reset.
Basically every time user uploads a file with fileInput, in the future he is forced to click on doneUpload button which resets the fileInput text area and puts his file in the storage (no alternatives please).
At this point, user can go back and upload another file which will be put in the storage too.
From my point of view, I have all uploaded files which I can easily retrieve and manipulate.
In other words, my question could be translated to:
how can I reset fileInput text area without resetting uploaded files?
I have tried creating a reactive value to store uploaded files, however it does not work as I expected.
Here I show you an example to simplify my situation.
library(shiny)
ui<-fluidPage(
uiOutput("uploadFile"),
actionButton("doneUpload","Done"),
tableOutput("exampleTest"))
server<-function(input, output){
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
observeEvent(input$doneUpload,{
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
})
reactiveFileUploaded<-reactiveValues(fileUploaded=NULL)
observe({
req(input$uploadFile)
reactiveFileUploaded$fileUploaded<-c(reactiveFileUploaded$fileUploaded,input$uploadFile$datapath)
})
#Test to verify if the storage has been created.
#Supposing two files have been uploaded, I retrieve the first one.
output$exampleTest<-renderTable({
read.csv2(reactiveFileUploaded$fileUploaded[1])
})
}
shinyApp(ui=ui,server=server)
Here is a much simpler approach for what you need - Just set multiple = TRUE in fileInput() so that it can accept multiple files at the same time. They can all be read and stored in a reactive in one go using lapply.
library(shiny)
ui <- fluidPage(
fileInput("files", "Upload", multiple = TRUE),
actionButton("done", "Done Upload"),
verbatimTextOutput("test")
)
server <- function(input, output) {
all_data <- eventReactive(input$done, { # stores data in all files as a list of dataframes
req(input$files)
setNames(lapply(input$files$datapath, read.csv), sapply(input$files$name, basename))
})
output$test <- renderPrint({
# all_data()[[1]] # access first file; others can be accessed the same way
lapply(all_data(), head) # shows first 6 rows of all files with their names
})
}
shinyApp(ui, server)
Quite Simple, just read the file before clearing the input, after the User has confirmed the Input.
Currently you are reading (and saving) the file the moment the User has selected it, no matter if he wants to upload it or not. Also it seems like you are somehow triggering a loop after selecting a file constantly adding the file to the reactive value.
Also with the datapath you are saving only the filepath not the filecontent, and the filepath also directs to the temp folder so I would recommend changing that routine as well.
But for the most basic functionalities you described the following code should get the job done. (I also added a simple check for content in the filesUploaded variable, so that the error message doesn't appear on start up)
library(shiny)
ui<-fluidPage(
uiOutput("uploadFile"),
actionButton("doneUpload","Done"),
tableOutput("exampleTest"))
server<-function(input, output){
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
reactiveFileUploaded<-reactiveValues(fileUploaded=c())
observeEvent(input$doneUpload,{
req(input$uploadFile)
reactiveFileUploaded$fileUploaded<-c(reactiveFileUploaded$fileUploaded ,input$uploadFile$datapath)
output$uploadFile<-renderUI({
fileInput("uploadFile", "Upload your file", accept = c(".csv"),placeholder="No files selected")
})
})
#Test to verify if the storage has been created.
#Supposing two files have been uploaded, I retrieve the first one.
output$exampleTest<-renderTable({
if(length(reactiveFileUploaded$fileUploaded)>1)
read.csv2(reactiveFileUploaded$fileUploaded[1])
})
}
shinyApp(ui=ui,server=server)

Creating a responsive actionButton for the user selcted checkboxes generated on the fly using dynamicUI [shiny/R]

I am stuck in a small problem related to shiny/R.
I am reading in a text file and displaying selective column names returned by grep search into the shiny application on the fly. For this, I am using the dynamicUI.
After the file is read in, the following function runs in server.R. It checks for specific colnames and displays this on the UI using uiOutput. Whatever column names are selected by the user, they are sent to another function for data processing and the plot it returned on the mainPanel.
server.R
output$Bait <- renderUI({
data <- input.data();
if(is.null(data)) return()
colnames <- names(data)
colnames = colnames[grep("*LFQ*",colnames,ignore.case=TRUE)]
# Creating the checkboxes using the above colnames
checkboxGroupInput("bait", "Choose Bait LFQ columns",
choices = colnames,
selected = colnames)
})
ui.R
shinyUI(
sidebarPanel(
uiOutput("Bait"),
),
mainPanel(
plotOutput(outputId = 'plot'),
)
)
Everything is fine, what I am trying to create is an action button for the checkboxes. Some files are big and have a longer list of column names >60, so whenever a checkbox is clicked, the whole function runs for processing and displays a plot. This gets unnecessary when the user has to deselect/select more than 10 columns.
An easy fix is, I kept selected=NULL but what I want is to add an actionButton after the checkboxGroupInput, so that user can select as many as checkBoxes but the function only runs when the GO button is pressed via the actionButton. If add a actionButton control after the checkbocGroupInput, it doesnt' works.
Can someone guide me in this regard. After working on this for sometime, now I am bit lost.
Thanks
Did you look into ?isolate? Lets say i want function initialFunction() only be evaluated if input$actionButtonis clicked.
observe({
input$actionButton # everything that triggers initialFunction() should come before isolate()
isolate({
# everything that should not trigger initialFunction() should come inside isolate()
initialFunction()
})
})

Can I save the old value of a reactive object when it changes?

Note: After coming up with the answer I reworded the question to make if clearer.
Sometimes in a shiny app. I want to make use of a value selected by the user for a widget, as well as the previous value selected for that same widget. This could apply to reactive values derived from user input, where I want the old and the new value.
The problem is that if I try to save the value of a widget, then the variable containing that value has to be reactive or it will not update every time the widget changes. But, if I save the the value in a reactive context it will always give me the current value, not the previous one.
How can I save the previous value of a widget, but still have it update every time the user changes the widget?
Is there a way that does not require the use of an actionButton every time the user changes things? Avoiding an actionButton can be desirable with adding one is otherwise unnecessary and creates excess clicking for the user.
Seeing as the session flush event method seems to be broken for this purpose, here is an alternative way to do it using an observeEvent construct and a reactive variable.
library(shiny)
ui <- fluidPage(
h1("Memory"),
sidebarLayout(
sidebarPanel(
numericInput("val", "Next Value", 10)
),
mainPanel(
verbatimTextOutput("curval"),
verbatimTextOutput("lstval")
)
)
)
server <- function(input,output,session) {
rv <- reactiveValues(lstval=0,curval=0)
observeEvent(input$val, {rv$lstval <- rv$curval; rv$curval <- input$val})
curre <- reactive({req(input$val); input$val; rv$curval})
lstre <- reactive({req(input$val); input$val; rv$lstval})
output$curval <- renderPrint({sprintf("cur:%d",curre())})
output$lstval <- renderPrint({sprintf("lst:%d",lstre())})
}
options(shiny.reactlog = TRUE)
shinyApp(ui, server)
Yielding:
Update This answer was posted before the advent of the reactiveValues/observeEvent model in shiny. I think that #MikeWise 's answer is the better way to do this.
After some playing around this is what I came up with. The ui.r is nothing special
ui.r
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId="XX", label="Choose a letter",choices=letters[1:5])
),
mainPanel(
textOutput("Current"),
textOutput("old")
)
)
))
"Current" will display the current selection and "old" displays the previous selection.
In the server.r I made use of three key functions: reactiveValues, isolate and session$onFlush.
server.r
library(shiny)
server <- function(input, output,session) {
Values<-reactiveValues(old="Start")
session$onFlush(once=FALSE, function(){
isolate({ Values$old<-input$XX })
})
output$Current <- renderText({paste("Current:",input$XX)})
output$old <- renderText({ paste("Old:",Values$old) })
}
The server.r works like this.
First, Values$old is created using the reactiveValues function. I gave it the value "Start" to make it clear what was happening on load up.
Then I added a session$onFlush function. Note that I have session as an argument in my server function. This will run every time that shiny flushes the reactive system - such as when the selectizeInput is changed by the user. What is important is that it will run before input$XX gets a new value - so the value has changed at the selectizeInput but not at XX.
Inside the session$onFlush I then assign the outgoing value of XX to Values$old. This is done inside an isolate() as this will prevent any problems with input$XX gets updated with the new values. I can then use input$XX and Values$old in the renderText() functions.

Shiny - Taking Text input into auxiliary function

I am new to Shiny and trying to build a more accessible input and output for a function I built. I am giving this to people that don't run R so trying to build something that runs my functions in the background and then spits out the answer.
I am having some trouble getting everything in the way that I want it unfortunately and dealing with a bunch of errors. However, here is my more pointed question:
The actual function that I want to run takes a name (in quotations as "Last,First") and a number.
PredH("Last,First",650)
So I want a shiny application that takes a name input and a number input that then runs this program and then spits back out a data table with my answer. So a couple of questions.
How do I get it in the right form to input into my equation on the server side script, do I need to return it in the function so it can be accessed using a function$table type access? (Right now I am just printing using cat() function in the console for the function but know that may not be usable for this type of application.
I want to return a dataframe that can be gotten at PredH14$table. How do I go about building that shiny?
Here is my code so far:
UI:
library(shiny)
shinyUI(pageWithSidebar(
# Application title
headerPanel("Miles Per Gallon"),
# Sidebar with controls to select the variable to plot against mpg
# and to specify whether outliers should be included
sidebarPanel(
textInput("playername", "Player Name (Last,First):", "Patch,Trevor"),
radioButtons("type", "Type:",
list("Pitcher" = "P",
"Hitter" = "H"
)),
numericInput("PAIP", "PA/IP:", 550),
submitButton("Run Comparables")
),
mainPanel(
textOutput("name")
)
Server:
library(shiny)
shinyServer(function(input, output) {
sliderValues <- reactive({
data.frame(
Name = c("name", "PA"),
Value = c(as.character(playername),
PAIP),
stringsAsFactors=FALSE)
})
name=input[1,2]
PAIP=input[2,2]
testing <- function(name,PAIP){
a=paste(name,PAIP)
return(a) }
output$name=renderText(testing$a)
})
I am not quite sure I understood your question 100% but I clearly see you are wondering how to pass the input of the UI into the server and maybe, the other way back.
In your server code, clearly you are not getting any input from the UI. Basically you have created three input variables in your ui.R:
1. input$playername
2. input$type
3. input$PAIP
And one output:
1. output$name
Just let you know, the function sliderValues <- reactive(..) is called every time there is any input from the input... like people click the dropdown list or people modifies words in the text box.
You can even get started without the submit button just to get started. But the existence of the submit button actually makes everything easier. Create a submit button for an input form. Forms that include a submit button do not automatically update their outputs when inputs change, rather they wait until the user explicitly clicks the submit button.
So you can put your code in a way similar like this:
# server.R
library(shiny)
shinyServer(function(input, output) {
sliderValues <- reactive({
result <- ... input$playername ... input$type ... input$PAIP
return(result)
})
output$name <- renderPlot/renderText (... sliderValues...)
})
# ui.R
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Miles Per Gallon"),
sidebarPanel(
textInput("playername" ... ),
radioButtons("type" ... ),
numericInput("PAIP" ... ),
submitButton("...")
),
mainPanel(
textOutput/plotOutput...("name")
)
))
In the end, check out the shiny example that might be what you want.
library(shiny)
runExample('07_widgets')

Resources