I am struggling to understand how isolate() and reactive() should be used in R Shiny.
I want to achieve the following:
Whenever the "Refresh" action button is clicked:
Perform a subset on a data.frame and,
Feed this into my function to recalculate values.
The subset depends on a group of checkboxes that the user has ticked, of which there are approximately 40. I cannot have these checkboxes "fully reactive" because the function takes about 1.5 sec to execute. Instead, I want to give the user a chance to select multiple boxes and only afterwards click a button to (a) subset and (b) call the function again.
To do so, I load the data.frame in the server.R function:
df1 <- readRDS("D:/././df1.RData")
Then I have my main shinyServer function:
shinyServer(function(input, output) {
data_output <- reactive({
df1 <- df1[,df1$Students %in% input$students_selected]
#Here I want to isolate the "students_selected" so that this is only
#executed once the button is clicked
})
output$SAT <- renderTable({
myFunction(df1)
})
}
How about something like
data_output <- eventReactive(input$button, {
df1[,df1$Students %in% input$students_selected]
})
Here is my minimal example.
library(shiny)
ui <- list(sliderInput("num", "rowUpto", min= 1, max = 10, value = 5),
actionButton("btn", "update"),
tableOutput("tbl"))
server <- function(input, output) {
data_output <- eventReactive(input$btn, {
data.frame(id = 1:10, x = 11:20)[seq(input$num), ]
})
output$tbl <- renderTable({
data_output()})
}
runApp(list(ui = ui, server = server))
Edit
Another implementation, a bit more concise.
renderTable by default inspects the changes in all reactive elements within the function (in this case, input$num and input$button).
But, you want it to react only to the button. Hence you need to put the elements to be ignored within the isolate function.
If you omit the isolate function, then the table is updated as soon as the slider is moved.
library(shiny)
ui <- list(sliderInput("num", "rowUpto", min= 1, max = 10, value = 5),
actionButton("btn", "update"),
tableOutput("tbl"))
server <- function(input, output) {
output$tbl <- renderTable({
input$btn
data.frame(id = 1:10, x = 11:20)[seq(isolate(input$num)), ]
})
}
runApp(list(ui = ui, server = server))
Use eventReactive instead:
data_output <- eventReactive(input$updateButton, {
df1 <- df1[,df1$Students %in% input$students_selected] #I think your comments are messed up here, but I'll leave the filtering formatting to you
})
output$SAT <- renderTable({
data_output()
})
And in your UI you should have something like:
actionButton('updateButton',label = "Filter")
Looking at ?shiny::eventReactive:
Use eventReactive to create a calculated value that only updates in
response to an event. This is just like a normal reactive expression
except it ignores all the usual invalidations that come from its
reactive dependencies; it only invalidates in response to the given
event.
Related
I would like to run a function that has a shiny app inside, but I can't.
Running this example separately, I first remove column one from my input data frame; then I run shiny to change whatever is necessary in the data frame and, when I close the window, a new object is saved with the changes; and finally I create a new column in the data frame.
This is an example script, but I would like that, when executing the function, the shiny window opens and some things are changed in the data frame for the user interactively. Could someone help?
library(shiny)
library(rhandsontable)
my_function <- function(x){
select <- x[,-1]
ui <- fluidPage(
fluidRow(
column(
width = 12,
rHandsontableOutput("myTable")
)))
server <- function(input, output, session) {
# dummy dataframe
df = select
# convert it to a "rhansontable" object
output$myTable <- renderRHandsontable({rhandsontable(df)
})
observeEvent(input$myTable, {
test_df = hot_to_r(input$myTable)
assign('my_data_frame',test_df,envir=.GlobalEnv)
# browser() # uncomment for debugging
})
}
shinyApp(ui, server)
my_data_frame2 <- my_data_frame %>%
mutate(new_column_test = "hello")
return(my_data_frame2)
}
my_function(mtcars)
Hi you almost made it you don't want to return anything but add the data simply using assign
library(shiny)
library(rhandsontable)
myapp_function <- function(data) {
ui <- basicPage(
actionButton("quit", label = "Close"),
actionButton("create", label = "Create copy"),
textInput("name","Set dataframe name", value = "my_data_frame"),
rHandsontableOutput("myTable")
)
server <- function(input, output, session) {
output$myTable <- renderRHandsontable({
rhandsontable(data)
})
observeEvent(input$create, {
assign( input$name, hot_to_r(input$myTable), envir=.GlobalEnv)
})
observeEvent(input$quit,{
stopApp()
})
}
## launch app
shinyApp(ui, server,options=c(shiny.launch.browser = .rs.invokeShinyPaneViewer))
}
## test
myapp_function(iris)
myapp_function(mtcars)
myapp_function(PlantGrowth)
I would suggest to create the ui and server outside of the myapp_function - otherwise it will become a very large function...also creating a function inside another function is not the best practise.
I'm working on an R shiny app structured like this:
library(shiny)
library(DT)
# global function
make_data = function(input){
data.frame(x = input$x, `x_times_2` = input$x*2)
}
ui <- fluidPage(
sliderInput("x", label = "Set x:", min = 1, value = 7, max = 10),
# Recalculates continuously, bad!
dataTableOutput("dtab"),
# Recalculates when inputs change, good!
# tableOutput("tab")
)
server <- function(input, output, session) {
reactive_data = reactive({
print("Recalculating Data")
make_data(reactiveValuesToList(input))
})
output$tab = renderTable({
reactive_data()
})
output$dtab = renderDataTable({
reactive_data()
})
}
shinyApp(ui, server)
My problem is that dataTableOutput("dtab") forces continuous recalculation of reactive_data whereas tableOutput("tab") (correctly) only recalculates when inputs change. Can someone help me understand why this happens?
I need to be able to pass inputs into a global function that makes a data frame which I then need to display. I want to use dataTableOutput() for the customization that DT offers but need it to only recalculate when any input is changed.
In this situation, you could use eventReactive() instead of reactive. Try this
reactive_data = eventReactive(input$x, {
print("Recalculating Data")
make_data(reactiveValuesToList(input))
})
i'm new in shiny, but i try to write a simple app. It will connect to DB, download DF and print it on site. And i got this. Now I want to pick range to save as csv a part of that DF.
So I have to input labels: Start and Stop, and Action Button.
I tried to find information how to implement that functionality, and i didn't. I found some info about observe function, but it's totaly not working in my example. When I do it as in official doc, after button click noting is happend.
My code:
ui <- fluidPage(
titlePanel("Skrypt"),
DT::dataTableOutput("table"),
numericInput("Start", "Start", 0),
verbatimTextOutput("v1"),
numericInput("Stop", "Stop", length(a)),
verbatimTextOutput("v"),
actionButton("button", "Generate For Range")
)
server <- function(input, output) {
# Filter data based on selections
output$table <- DT::renderDataTable(DT::datatable({
data <- myDat}))
}
shinyApp(ui, server)
And only what I tried to do is save Start and Stop as a variables after click button to use it in function to generate_csv(df, start_v, stop_v) as args.
Can someone explain me how to do that in simple way?
One solution uses eventReactive. It creates a calculated value that only updates in response to an event. In this case, the click on your button. That provides a data frame you can use in renderDataTable. Any code to filter data frame moves to the eventReactive named df.
myDat <- data.frame(A = 1:3, B = LETTERS[1:3]) # dummy data for testing
ui <- fluidPage(
titlePanel("Skrypt"),
DT::dataTableOutput("table"),
numericInput("Start", "Start", 1),
verbatimTextOutput("v1"),
numericInput("Stop", "Stop", 2),
verbatimTextOutput("v"),
actionButton("button", "Generate For Range")
)
server <- function(input, output) {
df <- eventReactive(input$button, {
# Test if filter is valid
if (input$Start >= input$Stop) stop("Start cannot be larger or equal than stop")
if (input$Start < min(myDat$A)) stop("Start cannot be less than smallest value")
if (input$Stop > max(myDat$A)) stop("Stop cannot be larger than largest value")
myDat[input$Start:input$Stop,] # use any filter you deem necessary
})
# Filter data based on selections
output$table <- DT::renderDataTable({
d <- DT::datatable(
data <- df()
)
})
}
shinyApp(ui, server)
I have been using R for awhile but R shiny seems like a completely foreign language to me.
I have an RShiny app utilizing the rhandsontable, which takes input from both another column in the rhandsontable and outside the rhandsontable to calculate a new column. Currently, the table does update everything when any entry from within the table is modified, but I'd like to minimize the risk of user error as much as possible, either by making the rhandsontable reactive to both changes inside and outside the table (preferred) or reset when a button is clicked.
The example below is modified from How to reset to default a reactive rhandsontable? but reproduced my issue. As soon as I add an observeEvent for the rhandsontable, even before I call the external input, the reset button no longer works.
To make the observeEvent reactive to both changes in the external input and the table changes, I tried to make a reactive input (e.g., listener <- reactive(c(input$two_by_two$changes$changes, input$reset_input))), put them both in the observeEvent (e.g., input$two_by_two$changes$changes | input$reset_input), which both result in Warning: Error in do.call: second argument must be a list error.
library(shiny)
library(rhandsontable)
server <- shinyServer(function(input, output, session) {
FACTOR <- reactive(input$factor_val)
DF <- data.frame(A = c(1, 2), B = c(2, 4), row.names = c("C", "D"))
DF1 <- reactiveValues(data=DF)
output$two_by_two <- renderRHandsontable({
input$reset_input
rhandsontable(DF1$data)
})
observeEvent(input$two_by_two$changes$changes,
{DF1$data <- hot_to_r(input$two_by_two)
DF1$data[,2] = DF1$data[,1]*FACTOR() }) })
ui <- shinyUI(fluidPage(
actionButton(inputId = "reset_input", label = "Reset"),
br(),
numericInput("factor_val","Multiplier Value",min=0,max=10,value=2),
rHandsontableOutput("two_by_two")
))
shinyApp(ui, server)
Thanks for any help again.
The reason you were not able to reset was that you had changed the reactive dataframe, but were never resetting it back. I added a new observe event to handle that. Please see if this is what you are looking for.
library(shiny)
library(rhandsontable)
server <- shinyServer(function(input, output, session) {
FACTOR <- reactive(input$factor_val)
DF <- data.frame(A = c(1, 2), B = c(2, 4), row.names = c("C", "D"))
DF1 <- reactiveValues(data=DF)
observeEvent(input$reset_input, {
DF1$data <- DF
})
output$two_by_two <- renderRHandsontable({
#input$reset_input
rhandsontable(DF1$data)
})
observeEvent(input$factor_val, {
req(input$factor_val)
DF1$data <- hot_to_r(req({input$two_by_two}))
DF1$data[,2] = DF1$data[,1]*FACTOR()
})
})
ui <- shinyUI(fluidPage(
actionButton(inputId = "reset_input", label = "Reset"),
br(),
numericInput("factor_val","Multiplier Value",min=0,max=10,value=2),
rHandsontableOutput("two_by_two")
))
shinyApp(ui, server)
I am trying to create a table using Shiny, where the user can click on a row in order to see further information about that row. I thought I understood how to do this (see code attached).
However, right now as soon as the user clicks the "getQueue" action button, the observeEvent(input$fileList_cell_clicked, {}) seems to get called. Why would this be called before the user even has the chance to click on a row? Is it also called when the table is generated? Is there any way around this?
I need to replace "output$devel <- renderText("cell_clicked_called")" with code that will have all sorts of errors if there isn't an actual cell to refer to.
Thank you for any advice!
ui <- fluidPage(
actionButton("getQueue", "Get list of queued files"),
verbatimTextOutput("devel"),
DT::dataTableOutput("fileList")
)
shinyServer <- function(input, output) {
observeEvent(input$getQueue, {
#get list of excel files
toTable <<- data.frame("queueFiles" = list.files("queue/", pattern = "*.xlsx")) #need to catch if there are no files in queue
output$fileList <- DT::renderDataTable({
toTable
}, selection = 'single') #, selection = list(mode = 'single', selected = as.character(1))
})
observeEvent(input$fileList_cell_clicked, {
output$devel <- renderText("cell_clicked_called")
})}
shinyApp(ui = ui, server = shinyServer)
minimal error code
DT initializes input$tableId_cell_clicked as an empty list, which causes observeEvent to trigger since observeEvent only ignores NULL values by default. You can stop the reactive expression when this list is empty by inserting something like req(length(input$tableId_cell_clicked) > 0).
Here's a slightly modified version of your example that demonstrates this.
library(shiny)
ui <- fluidPage(
actionButton("getQueue", "Get list of queued files"),
verbatimTextOutput("devel"),
DT::dataTableOutput("fileList")
)
shinyServer <- function(input, output) {
tbl <- eventReactive(input$getQueue, {
mtcars
})
output$fileList <- DT::renderDataTable({
tbl()
}, selection = 'single')
output$devel <- renderPrint({
req(length(input$fileList_cell_clicked) > 0)
input$fileList_cell_clicked
})
}
shinyApp(ui = ui, server = shinyServer)