Order of execution of observers in Shiny - r

Goal and Current Approach
I have a module, which can delete its own UI. I also want to be able to call this killing functionality from outside the module, hence I pass an additional reactive to the module's server logic and call the killing routine when this reactive fires.
So far so good. Now I want to implement a function which first kills all instances of my module and then adds new instances of this module and this is where I struggle with the design I have chosen.
Issue
What I would need is that before my input$add5 observer adds any new instances, all old instances are killed. This would be done by setting kill_switch(TRUE), but the problem is that before the observer in my module can react to that, the input$add5 observer continues to add new modules, which reset the kill_switch to FALSE and basically the old instances are never killed.
Basically, in the current design I would like that the observer on kill_switch reacts immediately after I change the flag and only after this is done, my add5 observer continues.
What I need
I think that my design is not optimal, thus any recommendations of how to set up the interface between the main application and the module would be highly appreciated.
Code
library(shiny)
boxer_ui <- function(id) {
ns <- NS(id)
div(
id,
id = ns("killme"),
style = "background-color:steelblue; font-size: xx-large; color: white")
}
boxer <- function(input, output, session, kill_switch) {
ns <- session$ns
observe({
req(kill_switch())
removeUI(paste0("#", ns("killme")))
})
}
ui <- fluidPage(actionButton("new", "new"),
actionButton("killall", "Kill All"),
actionButton("add5", "Kill All & Add 5"),
fluidRow(id = "content"))
server <- function(input, output, session) {
ids <- reactiveVal(0)
kill_switch <- reactiveVal(FALSE)
handler <- reactiveValues()
add_new <- function() {
kill_switch(FALSE)
ids(ids() + 1)
new_id <- paste0("id", ids())
insertUI("#content", "beforeEnd", boxer_ui(new_id))
handler[[new_id]] <- callModule(boxer, new_id, kill_switch)
}
observeEvent(input$new, {
isolate({
add_new()
})})
observeEvent(input$add5, {
isolate({
kill_switch(TRUE)
replicate(5, add_new())
})})
observeEvent(input$killall, kill_switch(TRUE))
}
shinyApp(ui, server)

One solution I could think of is to split remove / add as follows:
server <- function(input, output, session) {
ids <- reactiveVal(0)
kill_switch <- reactiveVal(FALSE)
add5 <- reactiveVal(FALSE)
handler <- reactiveValues()
add_new <- function() {
kill_switch(FALSE)
ids(ids() + 1)
new_id <- paste0("id", ids())
insertUI("#content", "beforeEnd", boxer_ui(new_id))
handler[[new_id]] <- callModule(boxer, new_id, kill_switch)
}
observeEvent(input$new, {
isolate({
add_new()
})})
observeEvent(input$add5, {
isolate({
kill_switch(TRUE)
add5(TRUE)
})})
observe({
req(add5())
isolate({
replicate(5, add_new())
add5(FALSE)
})
})
observeEvent(input$killall, kill_switch(TRUE))
}
This is however based on the assumption that the observer will never be interrupted by any other observer. Is that true? In this case I could also add priority parameters to ensure that the inner observer is fired first.
Can anybody conform that my assumption is right?

Related

How to automatically collapse code in RShiny app server (reactives, renders, etc)

I am working with a very large RShiny app and want to take advantage of code folding to organize the server.R file in this application. However, when I use the code-fold hotkey, it does not fold the various elements defined in the server (the reactive, render, etc. elements).
I'd like to be able to take this
# observe some things
observe({
query <- parseQueryString(session$clientData$url_search)
if (!is.null(query[['tab']])) {
updateTabItems(session, "sidebarMenu", selected = query[['tab']])
}
if (!is.null(query[['player']])) {
updateSelectInput(session, "profile", selected = query[['player']])
}
})
# Lots of "reactive" data fetching functions
league_stats <- reactive({
get1 <- fetch('yada')
return(get1)
})
# another reactive
shooting <- reactive({
get1$SHORT_MR_MADE<-sum(get1$short_mr_fgm,na.rm=T)
...
...
)}
and collapse it into this (or something like this) by just hitting the code-collapse hotkey.
# observe some things
observe({--})
# Lots of "reactive" data fetching functions
league_stats <- reactive({--})
# another reactive
shooting <- reactive({--})
Is this possible to do with R / RStudio? I would like to avoid using the 4 # signs #### above the function to code fold, as this will hide the shooting <- reactive({--}) strings as well, however I'd like to still have show (and just hide the code inside).
I will oftentimes wrap code in functions since functions collapse, however I cannot wrap RShiny reactive elements in functions (or, i'm not sure how), as it seems like this breaks the app.
Shiny reactives behave as other functions, but you need to take care about passing to them the input, session or other reactives (as function, not as value) they need.
As an illustration :
library(shiny)
generateUI <- function() {fluidPage(
actionButton("do", "Click Me"),
textOutput('counter')
)}
ui <- generateUI()
myobserver <- function(input,counter) {
observeEvent(input$do, {
cat('Clicked \n')
counter(counter()+1)
})
}
myformater <- function(counter) {
renderText(paste('count is',counter()))
}
server <- function(input, output, session) {
counter <- reactiveVal(0)
myobserver(input,counter)
output$counter <- myformater(counter)
}
shinyApp(ui, server)
Collapsed code :
Another way to do this without creating them as functions is to put an identifier above each code chunk:
library(shiny)
# Generate UI ----
generateUI <- function() {fluidPage(
actionButton("do", "Click Me"),
textOutput('counter')
)}
ui <- generateUI()
# Observer ----
myobserver <- function(input,counter) {
observeEvent(input$do, {
cat('Clicked \n')
counter(counter()+1)
})
}
# Formatter ----
myformater <- function(counter) {
renderText(paste('count is',counter()))
}
# Server ----
server <- function(input, output, session) {
counter <- reactiveVal(0)
myobserver(input,counter)
output$counter <- myformater(counter)
}
shinyApp(ui, server)
You will then be able to collapse code segments in between the two identifiers to view as shown below:

ShinyModules with "global" reactive values

I have a largish shiny app, where multiple elements change reactiveVals. Now I want to port the app to use shiny modules to be able to test it more appropriately. But I am not able to access the reactive values that are defined inside the server function.
MWE
A simple app that highlights my thought process so far is this counter app.
The app consists two modules: counter and display
counter increases the reactive counter value on the click of a button
display watches the counter and displays its output to a text-field
The main part of the app is a "central" reactive value called counter_reactive, which holds the current count.
This value is set by the counter and read by the display module elements.
library(shiny)
######################
# Counter Module
counter_UI <- function(id) {
ns <- NS(id)
tagList(
actionButton(ns("button"), "Increase Count")
)
}
counter <- function(input, output, session) {
observeEvent(input$button, {
counter_reactive(counter_reactive() + 1)
cat("Increase Counter by 1 to", counter_reactive(), "\n")
})
}
######################
# Display Module
display_UI <- function(id) {
ns <- NS(id)
tagList(
verbatimTextOutput(ns("text_output"))
)
}
display <- function(input, output, session) {
observe({
cat("Print Value of Counter\n")
output$text_output <- renderText(sprintf("Counter is now %i", counter_reactive()))
})
}
######################
# Rest of the Shiny App
ui <- fluidPage(
counter_UI("counter1"),
display_UI("display1")
)
server <- function(input, output, session) {
# Note that counter_reactive is defined inside the "global" server function,
# as multiple modules should read and write to it.
counter_reactive <- reactiveVal(0)
callModule(counter, "counter1")
callModule(display, "display1")
}
shinyApp(ui, server)
However, this app throws the error Warning: Error in counter_reactive: could not find function "counter_reactive".
Any ideas how to get/change the reactive value inside the module?
Rubber-Ducky-Debugging (aka SO-Question Writing Debugging) to the rescue, this works:
Simply passing the reactive value to the functions like so
counter <- function(input, output, session, counter_reactive) {
observeEvent(input$button, {
counter_reactive(counter_reactive() + 1)
cat("Increase Counter by 1 to", counter_reactive(), "\n")
})
}
display <- function(input, output, session, counter_reactive) {
observe({
cat("Print Value of Counter\n")
output$text_output <- renderText(sprintf("Counter is now %i", counter_reactive()))
})
}
server <- function(input, output, session) {
counter_reactive <- reactiveVal(0)
callModule(counter, "counter1", counter_reactive)
callModule(display, "display1", counter_reactive)
}

observeEvent with quoted handler expression

The app below contains a checkbox (default), a selectInput (letter), and two actionButtons (trigger1 and trigger2). In the server function, there are two observers:
observer 1 fires when the user clicks trigger1 AND the checkbox is unchecked, i.e. input$default == F.
observer 2 fires when the user clicks trigger2 AND the checkbox is checked, i.e. input$default == T.
Here is the app:
library("shiny")
ui <- fluidPage(
checkboxInput('default','Default'),
selectInput('letter', 'Letter', letters),
actionButton('trigger1', 'Trigger 1'),
actionButton('trigger2', 'Trigger 2')
)
server <- function(input, output, session) {
letter = reactive(input$letter)
#Observer 1
observeEvent(input$trigger1, {
req(!input$default)
print(letter())
})
#Observer 2
observeEvent(input$trigger2, {
req(input$default)
print(letter())
})
}
shinyApp(ui = ui, server = server)
Both observers share the same handler expression, i.e. print(letter()) and I was wondering if there was an alternative to having to write it out separately for each observer? This would be useful because the handler expression in my actual app is several lines long.
One way to do this would be to have a reactiveVal that changes in response to input$trigger1 or input$trigger2, depending on the value of input$default:
server <- function(input, output, session) {
letter = reactive(input$letter)
t = reactiveVal(0)
observeEvent(input$trigger1, {
req(!input$default)
t(t()+1)
})
observeEvent(input$trigger2, {
req(input$default)
t(t()+1)
})
observeEvent(t(), print(letter()), ignoreInit = T)
}
But I was wondering if it would be possible to store the expression in a variable and then evaluate it inside the observer using something like eval? Here is my attempt:
server <- function(input, output, session) {
letter = reactive(input$letter)
handler = quote(print(letter()))
#Observer 1
observeEvent(eventExpr = {if(isolate(input$default)) return(); input$trigger1},
handlerExpr = eval(handler))
#Observer 2 - not working
observeEvent(eventExpr = {if(!isolate(input$default)) return(); input$trigger2},
handlerExpr = eval(handler))
}
EDIT: This seems to work fine for observer 1 but not for observer 2 due to the isolate.
I'm also wary of using non-standard evaluation as I don't have much experience with it. What's wrong with my use of eval above and what is it doing exactly? Is there a better/safer alternative? Any guidance would be much appreciated.
Rather than making letter your reactive, can you make print your reactive? That way you can avoid having observeEvents for input$trigger1 and input$trigger2. Thus:
print <- reactive({
req(input$trigger1, input$trigger2, input$letter)
<your code here>
})

Shiny in R: How to properly use observe?

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()
})
})
}

Dealing with nested selectizeInputs and modules

I am having trouble with nested selectizeInputs, i.e. a group of select inputs where the selection in the first determines the choices in the second, which control the choices in the third, and so on.
Let's say I have an select1 that lets you choose a dataset, and select2 which lets you pick a variable in the dataset. Obviously the choices in select2 depend on the selection in select1. I find that if a user selects a variable from select2, and then changes select1, it doesn't immediately wipe out the value from select2, but instead it goes through a reactive sequence with the new value in select1, and the old value from select2, which is suddenly referencing a variable in a different dataset, which is a problem.
Example:
library(shiny)
ui =fluidPage(
selectizeInput('d',choices=c('mtcars','iris'),
label="Datasets"),
uiOutput("vars"),
htmlOutput("out")
)
server = function(input, output, session) {
output$vars <- renderUI({
req(input$d)
selectizeInput("v",choices=names(get(input$d)), label="Variables",
options=list(onInitialize=I('function() {this.setValue("");}')))
})
output$out <- renderUI({
req(input$d,input$v)
HTML(paste0("The max is ",max(get(input$d)[[input$v]])))
})
}
runApp(list(ui = ui, server = server))
On launch, select mpg, and displays max value.
Now, after selecting mpg, if you switch to iris, you will get a barely noticeable error, then it corrects itself. This is a toy example, so the error is insignificant, but there could easily be cases where the error is much more dire (as is the case with the app I am currently developing).
Is there a way to handle nested selectizeInputs such that changes in an upstream selectizeInput won't evaluate with old values of down stream selectizeInputs when changed?
Thanks
edit: This issue turns out to have to do more with modules than anything else I believe:
library(shiny)
library(DT)
testModUI <- function(id) {
ns <- NS(id)
DT::dataTableOutput(ns("out"))
}
testMod <- function(input, output, session, data) {
output$out <- DT::renderDataTable({
data()
},caption="IN MODULE")
}
ui =fluidPage(
selectizeInput('d',choices=c('mtcars','iris'),
label="Datasets"),
uiOutput("vars"),
testModUI("test"),
DT::dataTableOutput("test2")
)
server = function(input, output, session) {
output$vars <- renderUI({
req(input$d)
selectizeInput("v",choices=names(get(input$d)), label="Variables",
options=list(onInitialize=I('function() {this.setValue("");}')))
})
observe({
req(input$d,input$v)#,get(input$d)[[input$v]])
validate(
need(input$v %in% names(get(input$d)), 'Wait.')
)
callModule(testMod,"test",reactive(data.frame(v1=max(get(input$d)[[input$v]]))))
})
output$test2 <- DT::renderDataTable({
req(input$d,input$v)#,get(input$d)[[input$v]])
validate(
need(input$v %in% names(get(input$d)), 'Wait.')
)
data.frame(v1=max(get(input$d)[[input$v]]))
},caption="OUTSIDE MODULE")
}
runApp(list(ui = ui, server = server))
Hello you can put condition to check if your code is going to run, here you just need that input$v to be a valid variable from input$d, so do :
output$out <- renderUI({
req(input$d,input$v)
if (input$v %in% names(get(input$d))) {
HTML(paste0("The max is ",max(get(input$d)[[input$v]])))
}
})
# or
output$out <- renderUI({
req(input$d,input$v)
validate(
need(input$v %in% names(get(input$d)), 'Wait.')
)
HTML(paste0("The max is ",max(get(input$d)[[input$v]])))
})
EDIT with module, you can define your module with an expression to validate like this :
testMod <- function(input, output, session, data, validExpr) {
output$out <- DT::renderDataTable({
validate(need(validExpr(), FALSE))
data()
},caption="IN MODULE")
}
And call the module in the server with the expression in a function :
observe({
req(input$d,input$v)
callModule(
module = testMod,
id = "test",
data = reactive({ data.frame(v1=max(get(input$d)[[input$v]])) }),
validExpr = function() input$v %in% names(get(input$d))
)
})

Resources