I am working with shiny for R. At the beginning of my program I am creating a dataframe that is stored in reactiveValues() .
myReactiveDataframe <- reactiveValues()
myReactiveDataFrame[["df"]] <- someDataFrame
Upon interaction with the App through the user content of the dataframe is modified and then stored in reactiveValues() as well.
Something like
myReactiveDataframe[["modified"]] <- myReactiveDataFrame$df[MyReactive....]
Later on in my App I have some observe() that work with myReactiveDataframe$modified.
My Problem is: On the start of the App myReactiveDataframe$modified evaluates to NULL because it is not created yet. That means that many of the observers get triggered and crash of course because the input is NULL and not a dataframe. To prevent that I have to write a if/else at the beginning of each observe that checks if the variable of interest is.null(....
Very annoying. Any ideas how not to do that?
Related
I have the following issue with the behaviour of R shiny's numeric input behaviour. Consider the following snippet:
ui <- basicPage(
numericInput("example","Example",value=0),
verbatimTextOutput("changelog")
)
server <- function(input,output){
output$changelog <- renderPrint(input$example)
}
shinyApp(ui,server)
Suppose that I want to update the example input field to 12345. My issue is that the default event listener would react to every keystroke. Thus the input field would be set to 1, 12,123 and 1234 before I finally get the desired value of 12345. Each numeric input set would be followed by an expensive computation - so this is a very undesirable behaviour.
What I am after is modifying this behaviour so that the event listener only reacts to the numeric input when the user hits enter or leaves the input field. I have currently two approaches to this:
Use a reactiveValue with an update actionButton so that the input is updated only when the user clicks update. I find this an inelegant solution and only shirks the original problem without solving it.
Modify the local shiny.js file directly and remove the keyup.textInputBinding event. That creates another issue with running the shiny app on other computers and it would make this modified behaviour uniform for all numericInput.
I'm wondering if anyone has a solution/suggestion to this? Preferably something that does not involve changing the local shiny.js file. I'm guessing a solution would involve using shinyjs::runjs to manually unsubscribe the keyup.textInputBinding event - but I don't know enough JavaScript to execute it.
You can slow frequent invalidation down with debounce or throttle. In your case, my first guess would be debounce: Debouncing means that invalidation is held off for millis milliseconds.
The reactive expression will only be validated until that time window has passed without a subsequent invalidation which may have an effect like this: ooo-oo-oo---- => -----------o-
In your case:
library(shiny)
ui <- basicPage(
numericInput("example","Example",value=0),
verbatimTextOutput("changelogImmediate"),
verbatimTextOutput("changelog")
)
server <- function(input,output){
exampleInput <- reactive(input$example) %>% debounce(1000)
# debouncedExampleInput <- exampleInput
output$changelogImmediate <- renderPrint(input$example)
output$changelog <- renderPrint(exampleInput())
}
shinyApp(ui,server)
I am very new to Shiny and R in general and I am building an app that allows users to import data, select their variables, number of trees.. ect and then run that through a random forest script and have it display the outputs. Right now I am just working on the inputs, however, I am running into a problem. The user can import a CSV but then they cannot select their variables (headers from csv). I am trying to make it reactive so the user first must import their csv before the option of selecting their variables pops up (seems simple).
Here is my code right now:
ui.R
server.R
Error in Console
I am probably just making a silly mistake because I am unfamiliar with Shiny but your help would be much appreciated.
A little background info
First off, I recommend you read this through to get a good understanding of reactivity in Shiny: Shiny Reactivity Overview. This helps a lot with variable scoping in a Shiny context too: Scoping Rules in Shiny Apps.
The issue
I believe this issue is due to you defining the variable file_to_read within the scope of shiny output: output$input_file in your server.R file. When the function read.table() looks for the variable file_to_read, it doesn't exist since it is only defined within the scope of the shiny output.
Try making a reactive value and then assigning the input$file value to it once it is uploaded by the user. You'll also have to convert your dat1 variable to a shiny reactive since you can only read reactive values in the context of other reactive sources (e.g. observe(), reactive(), observeEvent(), etc.)
file_to_read <- reactiveVal(NULL) # Set the value to NULL to initialize
output$input_file <- renderTable({
if (is.null(input$file)) return () # Check there is an input file
file_to_read(input$file) # Set the reactiveVal to the input file
})
# Make dat1 a reactive so it can be read within a shiny reactive
dat1 <- reactive({
if(is.null(file_to_read()) return (NULL) # Check for input file in reactiveVal
read.table(file_to_read()$datapath, sep = input$sep, header = input$header)
})
# Make an eventReactive to capture when there is read.table() data from dat1
reactive1 <- eventReactive(dat1, {
if (is.null(dat1)) return ()
D <- colnames(dat1)
return (list(D, D[1]))
})
I didn't test this code since you posted your data in image format and I don't have an input file, but hope this helps your error.
I stored several tables in .rds files which I would like to import using readRDS in a Shiny session. Therefore I defined the following global function:
get.station <- function(sname){
file.name <- paste(sname".rds", sep="")
return(readRDS(file.name))
}
within the server function I define the reactive value:
st.reactive <- reactiveValues(get.station(input$station.sel))
where input$station.sel comes from the ui using selectInput(...). This results in the following error message:
Operation not allowed without an active reactive context.
(You tried to do something that can only be done from inside
a reactive expression or observer.)
This error message even does not disappear if I define this function using reactive():
get.station <- reactive({
file.name <- paste(input$station.sel".rds", sep="")
return(readRDS(file.name))
})
and within the server:
st.reactive <- reactiveValues(data=get.station())
Do you have any help?
You've got the right idea, just put the function inside a reactive or observe_ function. While you can define reactiveValues in the initial call, its best, in my opinion, to create the reactive values object empty and then do all your assignments with the <- notation.
The confusion comes from the fact that, despite it's name, reactiveValues is not a reactive expression. It generates a reactiveValues object that reactive values can be stored in, but it only runs once and does not check whether its values are invalidated.
In your case, I'd do the following:
rv <- reactiveValues()
rv$st.reactive <- observe({get.station(input$station.sel)})
Previously, I had built a shiny dashboard with chart outputs that worked just fine and looked like the following:
output$someName <- renderGvis({
input$inputButton
data <- isolate(myData(function here))
donut <- gvisDonut({...})
return(donut)
})
Since the addition of observeEvent and eventReactive, I've not been able to get it to work the same as before. Essentially, the output$someName is a chart that is dependent on multiple inputs, and each time the user clicks on the inputButton, I need renderGvis to re-evaluate. The function should NOT re-evaluate when any of the other inputs change, just when the button is pressed.
I've had some luck getting observeEvent to run on input$inputButton click, however, each time I change any of my input parameters, the query is quickly rerun without having to press the button. Any takers here?
More detailed below:
output$someName <- renderGvis({
input$inputButton
data <- isolate(dataGrabber({})) # function that takes input and returns data frame using RMySQL
isolate(simpleChart(data = data)) # simpleChart is a function to produce a gvisCalendar chart.
OK...found an answer to this if anyone ever has this problem. The issue, which for some reason I had not encountered in the past, is that the isolate function now runs regardless of whether or not the value for actionButton is 0 or not. I believe in the past, it wouldn't run until actionButton had a value greater than 0.
The simple fix was:
output$someName <- renderGvis({
input$inputButton
if (input$inputButton == 0)
return()
isolate({ code to isolate})
})
So this is somehow a follow up to my previous: Automatic GUI Generating in R Shiny wher I posted the solution to how to generate elements iteratively.
Now I am unable to recieve/check which actionbuttons have been pressed and perform some action upon press.
In general, there is a function that generates the buttons and sends it to the ui.R:
output$generateImages <- renderUI({
(...)
i <-1
for(dir in folders){
(...)
txt<-paste0("rep",i)
pp<-pathNameToImage
LL[[i]] <- list(actionButton(txt,icon=imageOutput(pp,width="100px",height="100px"),label=dir))
i<-i+1
}
return(LL)
}
in ui.R I have:
uiOutput('generateImages')
And it displays the buttons fine by accumulating them into the list called "LL".
I have tried looking for tutorials and examples but was not able to find how it is done with images, and how to later recieve input from buttons that were not created "by hand", but iteratively.
How do I access these buttons in "observe" so that I can perform a action? I have tried input$generateImages, input$LL and few others, but all of them had a value of NULL.
You'll need to access them by their unique ID. The first argument passed to actionButton is its ID. That's what you'll need to use to get it as input.
So:
LL[[i]] <- list(actionButton("SomeID"))
when you assign it, then
input[["SomeID"]]
when you want to reference it. Of course, you can use a variable instead of a hardcoded string for the ID.