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)
Related
I want to filter columns with checboxInput.
library(shiny)
df <- data.frame(Column1 = c(1,2,3),
Column2 = c(1,2,3))
ui <- fluidPage(
checkboxInput("checkbox1", "checkbox1"),
checkboxInput("checkbox2", "checkbox2"),
tableOutput("table")
)
server <- function(input, output, session) {
output$table <- renderTable(df[,c(input$checkbox1,input$checkbox2)])
# or like this
# v_checkboxes <- reactive(input$checkbox1,input$checkbox2)
# output$table <- renderTable(df[,c(v_checkboxes)])
}
shinyApp(ui, server)
I got this errors:
With inputs passed directly to df[,]
Warning: Error in <-: invalid subscript type 'list'
With v_checkboxes
Warning: Error in $: Can't access reactive value 'checkbox2' outside of reactive consumer.
i Do you need to wrap inside reactive() or observe()?
I really don't know what I am missing.
PS. It can be checkboxGroupInput also
The issue here is that when nothing is checked, the result would be a
0-column data frame – and that doesn’t play well with renderTable(). To
fix that, add a req() around the vector you’re using as a subscript. That
will prevent rendering if all values are FALSE. Also, as
#Ben mentioned in their comment, use drop = FALSE to prevent the result
from being a vector when only a single column is selected.
Here’s the revised app:
library(shiny)
df <- data.frame(
Column1 = c(1, 2, 3),
Column2 = c(1, 2, 3)
)
ui <- fluidPage(
checkboxInput("checkbox1", "checkbox1"),
checkboxInput("checkbox2", "checkbox2"),
tableOutput("table")
)
server <- function(input, output, session) {
output$table <- renderTable({
df[, req(c(input$checkbox1, input$checkbox2)), drop = FALSE]
})
}
shinyApp(ui, server)
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 currently have the following shiny app and the intention is to add the text input as a new row into the dataset. Currently, the new text input is overriding the old input instead of appending to the end. I understand that's happening because I'm referring to df in the eventReactive, which is the originally seeded empty data frame, but my various attempts around that have led to errors.
Does somebody know of a way to fix that? Bonus if there is a way to clear the text input box after hitting the submit button.
library(shiny)
colClasses = c("factor", "numeric")
col.names = c("Player", "1")
df <- read.table(text = "",
colClasses = colClasses,
col.names = col.names)
ui <- fluidPage(
# Application title
titlePanel("Random Draft Order"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
textInput("AddPlayer",
"Add Player",
""),
actionButton("submit", ("Submit"))
),
# Show a plot of the generated distribution
mainPanel(
tableOutput("racingbars")
)
)
)
server <- function(input, output) {
actionButton("submit", ("Submit"))
FinalData = eventReactive(input$submit,{
df = rbind(df, data.frame("Player" = input$AddPlayer, "X1" = ""))
})
output$racingbars <- renderTable({
FinalData()
})
}
# Run the application
shinyApp(ui = ui, server = server)
You could try this creating a reactiveVal that will store the data.
First, you can initialize your data.frame df as a reactiveVal:
df <- reactiveVal(data.frame(Player = character(),
X1 = character()))
To update the reactiveVal you can use:
df(new_dat)
And to reference the data later on, use:
df()
In your example, an observeEvent can be triggered by your submit button. When that happens, you can add your row of data to the reactiveVal data.frame similar to what you have done. The output can then point to the reactiveVal data.frame to display the data.
I also added updateTextInput to the observeEvent to clear the text input after the submit button is pressed. Note this requires session in the server function declaration.
server <- function(input, output, session) {
df <- reactiveVal(data.frame(Player = character(),
X1 = character()))
observeEvent(input$submit, {
new_dat <- rbind(df(), data.frame(Player = input$AddPlayer, X1 = ""))
df(new_dat)
updateTextInput(session, "AddPlayer", value = "")
})
output$racingbars <- renderTable({
df()
})
}
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 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.