I have an app that has a few dependent selectInputs, so if you choose something in the first, the second should update to a specific value. That works fine. However! Now I want to force a specific combination on the two selects that do not correspond to the update logic, but after I update the two selects, the change of the first triggers an update of the other and I end up with the wrong result. Also after the forced combination has been applied, if a new change to the first select is done, then the "old" rule should reapply.
library(shiny)
ui <- fluidPage(
selectInput("A_sel","select" ,c("A","B","C","D"),"A",FALSE)
,selectInput("B_sel","same" ,c("A","B","C","D"),"A",FALSE)
,actionButton("ForceCombi","force C and D")
)
server <- function(input, output, session) {
observeEvent(input$A_sel,{
updateSelectInput(session,"B_sel",selected = input$A_sel)
})
observeEvent(input$ForceCombi,{
updateSelectInput(session,"A_sel",selected = "C")
updateSelectInput(session,"B_sel",selected = "D")
})
}
shinyApp(ui, server)
EDIT - Timer solution:
I set a timestamp to each activation and see which was the last to be activated, except if the time difference is less than a sec then I assume that the button was pressed which activated the select. Then the return from that reactive is decides how to update the selects. A bit of a hack:
library(shiny)
library(dplyr)
ui <- fluidPage(
selectInput("A_sel","select",c("A","B","C","D"),"A",FALSE)
,selectInput("B_sel","same as above",c("A","B","C","D"),"A",FALSE)
,actionButton("A_to_B","force C and D")
)
server <- function(input, output, session) {
but <- eventReactive(input$A_to_B,{tibble(src = "but", time = Sys.time())})
sel <- eventReactive(input$A_sel ,{tibble(src = "sel", time = Sys.time())})
src <- eventReactive(c(input$A_to_B,input$A_sel),{
df <- try(rbind(but(),sel()))
if(typeof(df) == "character") return("sel")
if(abs(difftime(df$time[1],df$time[2],units = "sec")) < 1) return("but")
df %>% arrange(time) %>% pull(src) %>% last -> df
return(df)
})
observe({
src <- src()
if(src == "sel") {
updateSelectInput(session,"B_sel",selected = input$A_sel)
} else if (src == "but") {
updateSelectInput(session,"A_sel",selected = "C")
updateSelectInput(session,"B_sel",selected = "D")
}
})
}
shinyApp(ui, server)
Here's a simpler implementation of your timestamp idea. I have set the threshold to 0.5 seconds but actual threshold can only be determined after considering other reactive dependencies in the app. You should also look into the priority arguments of observe and observeEvent using which you could potentially control the execution sequence of reactives.
Having said that, I still have a feeling that there is a better way to do this. I think looking at ?shiny::throttle and ?shiny::debounce could help as well.
library(shiny)
ui <- fluidPage(
selectInput("A_sel","select", c("A","B","C","D"),"A",FALSE)
,selectInput("B_sel","same", c("A","B","C","D"),"A",FALSE)
,actionButton("ForceCombi", "force C and D")
)
server <- function(input, output, session) {
tstamp <- reactiveValues(t = Sys.time())
observeEvent(input$A_sel, {
req((Sys.time() - tstamp$t) > 0.5)
tstamp$t <- Sys.time()
updateSelectInput(session,"B_sel", selected = input$A_sel)
})
observeEvent(input$ForceCombi, {
updateSelectInput(session,"A_sel", selected = "C")
updateSelectInput(session,"B_sel", selected = "D")
tstamp$t <- Sys.time()
})
}
shinyApp(ui, server)
Related
I would need some help with the missing code here:
selectInput("portfolio",
"Portfolio:",
c("p1","p2"))
## missing code:
## if input$portfolio == "p1" do a bunch of calculations and spit out the variable var (a tibble).
# variable var goes into a reactiveVal...
table <- reactiveVal()
table(var)
On the server you can, set table (not a great name, perhaps use something else, like my_table? to reactiveValues(), and then observe for changes in input$portfolio
table <- reactiveValues(var=NULL)
observeEvent(input$portfolio, {
if(input$portfolio == "p1") {
table$var = <- someFunction()
}
})
Here is a full example, using mtcars
library(shiny)
ui <- fluidPage(
selectInput("make","Make:", choices = rownames(mtcars)),
tableOutput("subtable")
)
server <- function(input, output, session) {
subtable <- reactiveValues(var=NULL)
observeEvent(input$make, {
subtable$var = dplyr::filter(cbind(makes,mtcars), makes == input$make)
})
output$subtable <- renderTable(subtable$var)
}
shinyApp(ui, server)
So I have a complex shiny app with some internal default data. I added the possibility to update these internal data with new files (uploaded with fileInput()). I want that only after I click on a button, the default data and the new data merge together. I find this solution (just add any file in fileInput() to make it works).
library(shiny)
ui <- fluidPage(
fluidRow(
column(3, fileInput("fileupaziendedata", "Carica il file csv")), #just load any file
column(3, actionButton("mergeaziende", "Unisci"))), #the button that merges
hr(),
dataTableOutput("summary_table")
)
server <- function(input,output){
data = reactive({
data.frame(id = c(1:5), lett = c("A", "B", "C", "D", "E"))
})
datafromfile = reactive({
data.frame(id = c(6,7), lett = c("F", "G"))
})
data2 = reactive({
if(!is.null(input$fileupaziendedata) && input$mergeaziende > 0){
rbind(data(), datafromfile())
}else{
data()
}
})
output$summary_table <- renderDataTable({
data2()
})
}
shinyApp(ui=ui, server=server)
Now my problem is that since I used input$mergeaziende > 0 condition, after the first button press, it will be always greater than 0. Is there a way to prevent this?
I've seen this type of question come up before. Basically, how to "reset" the action button value. Here's my general solution. I reset a reactiveVal instead of trying to manipulate the input. Here the input will reset after 5, but it can be changed to something else (like when your data reactive triggers):
library(shiny)
ui <- fluidPage(
actionButton('click' ,'Click'),
textOutput('counter_out')
)
server <- function(session, input, output){
# initialize at - 0
# this is the value to observe (not input$click)
counter <- reactiveVal(0)
output$counter_out <- renderText(counter())
#increment per click
observeEvent(input$click, counter(counter() + 1))
observe({
#WHEN you want the count reset
if(counter() > 5) counter(0)
})
}
shinyApp(ui, server)
I have this question: In a Shiny App, I construct a varible with a reactive(). The thing is that, in the midle of this process (that is a long one) I construct other varibles that I need too.
For example:
#---------------UI------------------
ui <- navbarPage(
title = "example",
tabPanel('panel',
tableOutput("my_table"),
tableOutput("colum_names"))
)
#---------------SERVER------------------
server <- function(input, output) {
a <- reactive({
df_1 <- data.frame("fc"=c(1,2,3), "sc"=c(1,2,3), "tc"=c(1,2,3) )
df_2 <- subset(df_1,select=-c(fc))
column_names <- colnames(df_2)
df_3 <- df_2*2
df_3
})
output$my_table = renderTable({
a()
})
output$colum_names = renderTable({
df_column_names = data.frame(column_names())
df_column_names
})
}
#---------------APP------------------
shinyApp(ui = ui, server = server)
In this (very short) example, I would need the variable "a" (of course) and the variable "column_names". I can do something like create a new reactive that reproduce all the process until the line that contain "column_names" and finish it there. But the process is too long and I prefer to do it more "eficiently".
Any idea??
Thank you so much!
The process you're describing is correct : instead of assigning variables, just assign reactives and Shiny will handle the depedencies between them.
Note that in the example you provided, reactives aren't needed because the content is up to now static.
library(shiny)
#---------------UI------------------
ui <- navbarPage(
title = "example",
tabPanel('panel',
tableOutput("my_table"),
tableOutput("column_names"))
)
#---------------SERVER------------------
server <- function(input, output) {
df_1 <- data.frame("fc"=c(1,2,3), "sc"=c(1,2,3), "tc"=c(1,2,3) )
a <- reactive({subset(df_1,select=-c(fc))})
column_names <- reactive({colnames(a())})
output$my_table = renderTable({a()})
output$column_names = renderTable({column_names()})
}
#---------------APP------------------
shinyApp(ui = ui, server = server)
I found a interesting answer to my own question: if you want to do something like that, you can use "<<-" instead of "<-" and it save the variable when you are working insede a function (like reactive()). Let´s see:
#---------------UI------------------
ui <- navbarPage(
title = "example",
tabPanel('panel',
tableOutput("my_table"),
tableOutput("colum_names"))
)
#---------------SERVER------------------
server <- function(input, output) {
a <- reactive({
df_1 <- data.frame("fc"=c(1,2,3), "sc"=c(1,2,3), "tc"=c(1,2,3) )
df_2 <- subset(df_1,select=-c(fc))
column_names <- colnames(df_2)
# HERE THE SOLUTION!!
column_names_saved <<- column_names
df_3 <- df_2*2
df_3
})
output$my_table = renderTable({
a()
})
output$colum_names = renderTable({
df_column_names = data.frame(column_names_saved)
df_column_names
})
}
#---------------APP------------------
shinyApp(ui = ui, server = server)
Then, into the funtion you must continues with the variable "column_names", but when you need to use it later, you can use "column_name_saved". (just be carefull with one thing: onece you save the variable into the funtion, you canot change it)
Thanks!!!
I have a problem with my code. I have 2 input files which I want to read with click of button and a numeric input which contains a filter value for the output of the table being created from the 2 files (after manipulating the data). The whole process (read files + create table + filter) right now is executed every time the user click the button. I want to do only the filter action if the input files doesn't change, because the process takes long time.
After the first click I want to do only the filtering command when the numeric input changes, unless the input files is also changed by the user.
The following code reproduces my problem:
library(shiny)
library(data.table)
server <- function(input, output, session) {
output$table1 <- renderDataTable({
input$gobtn
isolate({
infile1 <<- input$f1
infile2 <<- input$f2
if (is.null(infile1) || is.null(infile1)) {
return (NULL)
}
else {
calc()
}
})
})
calc <- function() {
inf1 <<- fread(infile1$datapath)
inf2 <<- fread(infile2$datapath)
# do some process with files data.....
my_table <- as.data.table(rbind(inf1, inf2))
setnames(my_table, c('name', 'rank'))
result <- my_table[rank > input$rank]
return(result)
}
}
ui <- basicPage(
fileInput("f1", "f1"),
fileInput("f2", "f2"),
numericInput("rank", "show rank only above :", value = 6),
actionButton("gobtn", "show"),
dataTableOutput('table1')
)
shinyApp(ui = ui, server = server)
The way to use reactivity is to break things into parts, so that you only need to update what is necessary. The first step in your pipeline is reading and processing the files. This seems like a good reactive: if they don't change, nothing happens, but when they change, everything that needs to be recalculated is recalculated. The next step is filtering, when the filter variable changes we want to refilter the data. Then we can just put that in the output.
server <- function(input, output, session) {
processedData <- reactive({
req(input$f1,input$f2)
inf1 <- fread(input$f1$datapath)
inf2 <- fread(input$f2$datapath)
# do some process with files data.....
my_table <- as.data.table(rbind(inf1, inf2))
setnames(my_table, c('name', 'rank'))
my_table
}
filteredData <- reactive({
req(input$rank)
processedData()[processedData()$rank > input$rank]
})
output$table1 <- renderDataTable({
input$gobtn
isolate({
filteredData()
})
})
}
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.