Is there a way to undo callModule? The use case is I have a variable number of modules - the number responds to user selection. Let's say the user chooses 10, then a different 10 - is there a way to remove the original 10? Does it happen automatically? I'm worried that memory is being occupied and not released.
It's a bit tricky creating an reprex, here's a snippet of what I mean though:
observeEvent(
input$people
, {
input$people %>%
walk(
~callModule(people_info_server, .x)
)
}
)
Every time the vector input$people changes, the module with people_info_server gets called on each element of input$people and generates a page of information for each person.
What I'd like to do is something like this:
observeEvent(
input$people
, {
remove_existing_calls(people_info_server) # Not sure how to define this function
input$people %>%
walk(
~callModule(people_info_server, .x)
)
}
)
I suppose you would have to create a "destructor" for your module and make sure that the client of the module (the "main app") calls the destructor at the right time. For example, let us suppose the module returns the destructor as a closure.
library(shiny)
## module definition
module_with_destructor <- function(input, output, session) {
output$plot <- renderPlot({
plot(1:input$n)
})
destructor <- function() {
# add more cleanup logic here
output$plot <- NULL
}
return(destructor)
}
We now need to make sure that the main app executes the destructor when memory should be freed.
## main app (client)
myenv <- new.env()
observeEvent(input$create_module, {
if (is.null(myenv$destructor))
myenv$destructor <- callModule(module_with_destructor, "module_id")
})
observeEvent(input$destroy_module, {
if (!is.null(myenv$destructor))
myenv$destructor()
})
Of course, you will have to implement some extra logic in order to use this idea with a dynamic number of modules. However, in your case you could just create a list that collects all the individual destructors and then iterate over them.
Related
To all R Shiny experts: Which of the following three server functions would you rate first, second and third - and why?
I had an intensive discussion today about which of the three solutions comes closest to "best practice" Shiny app design. (While they all three work the same.)
For example, version C seems odd to me since overwriting render functions conditionally is unnecessary (since conditional output rendering is what these functions were made for).
The original app contains much more logic when dealing with input values, of course. I simplified the example to make the differences obvious.
library(shiny)
ui <- fluidPage(
shiny::radioButtons(
inputId = "some_input",
label = "Please choose:",
choices = c("something", "nothing")
),
shiny::textOutput(
outputId = "some_output"
)
)
# version A: all logic within rendering function
server <- function(input, output, session) {
output$some_output <- shiny::renderText({
if(input$some_input == "something"){
# imagine some complex logic here
"some value was chosen"
} else {
NULL
}
})
}
# version B: most logic within input observer,
# using reactive session userData
server <- function(input, output, session) {
session$userData$memory <- shiny::reactiveValues(
"stored_value" = NULL
)
output$some_output <- shiny::renderText({
session$userData$memory$stored_value
})
shiny::observeEvent({
input$some_input
}, {
if(input$some_input == "something"){
# imagine some complex logic here
session$userData$memory$stored_value <- "some value was chosen"
} else {
session$userData$memory$stored_value <- NULL
}
})
}
# version C: all logic within observer,
# setting the rendering function conditionally
server <- function(input, output, session) {
shiny::observeEvent({
input$some_input
}, {
if(input$some_input == "something"){
# imagine some complex logic here
output$some_output <- shiny::renderText({ "some value was chosen" })
} else {
output$some_output <- shiny::renderText({ NULL })
}
})
}
shinyApp(ui = ui, server = server)
I am by no means a Shiny expert but as "best" isn't defined, I figured I would give my opinion based on the apps I've created (no documentation provided to support).
Order from best to worst:
A
B
C
Reasoning:
C: although having output$some_output in multiple places works, it is never good practice to do this and just creates confusion in the code
B: the observeEvent is repetitive as the renderText() is designed to observe when a reactive variable gets changed. I know you have a more complicated app but storing to reactiveValues in this example is over the top without gaining any benefit.
A: Very simple, basic code that works seamlessly. You could also argue you could take the if statement out of the renderText() and wrap it in a reactive() to keep it cleaner but they accomplish the same thing.
I'd be curious if anyone would do time studies or has some actual documentation to back up a "best" to "worst".
I have developed a Shiny app that allows the user conditional selection of some dependent events. A very simplified toy example is below to help illustrate my question/problem.
In my real problem, the server code contains multiple computationally expensive procedures that are optional to run. There is a "baseline" function that must run to produce output and then firstObject or secondObject take that as input and produce more output if it is selected by the user to do so.
Each function can take upwards of 30 to 40 minutes. So, I wrote the code to allow the user to select using the checkInputBox which functions they want to run and then after selecting them, there is a single action button that runs them all allowing the user to leave and let the process take its course over many hours. This was more convenient than having an actionButton associated with each possible event.
The code below is successful in yielding all the desired output. But, I am not sure from a design point of view if it is "right". In my toy example, the code is simple, but suppose the code for baseObject takes 30 minutes to run. While baseObject is running, the code for firstObject and secondObject were also executed because they depend on the same action button. But, they cannot do anything until the function for baseObject is done. Similarly secondObject cannot do anything until firstObject is done.
Again, this all works and yields the correct output (in my real code as well as in the toy code). But, is there a way to maintain the single action button, but for firstObject to not do anything UNTIL baseline Object has produced its output and then secondObject would wait for firstObject to yield its output if the user selected it.
My worry is that I am creating additional computational overhead in the firstObject is trying to do something it cannot do until baseObject is done and it is cycling over and over until it can properly execute.
I know I can create different action buttons. For instance I could create an action button for baseline and then the user could wait until it is done and then click the action button for firstObject and so on. But, functionally this would not work as in the real problem this allows the entire selected process to run, which can take hours and the user does not need to be in front of their machine.
Thank you and I hope this code helps illustrate the problem as I have described it.
ui <- {
fluidPage(
h3('Run Stuff'),
checkboxInput("runModel1", "Model 1"),
checkboxInput("runModel2", "Model 2"),
actionButton('runAll', 'Run Models'),
verbatimTextOutput("out1"),
verbatimTextOutput("out2")
)
}
server <- function(input, output, session) {
baseObject <- eventReactive(input$runAll, {
if(input$runModel1){
runif(100)
}
})
firstObject <- eventReactive(input$runAll, {
if(input$runModel1){
runif(100) + baseObject()
}
})
secondObject <- eventReactive(input$runAll, {
if(input$runModel2){
runif(100) + firstObject()
}
})
output$out1 <- renderPrint({
if (input$runModel1)
firstObject()
})
output$out2 <- renderPrint({
if (input$runModel2)
secondObject()
})
} # end server
shinyApp(ui, server) #run
Two things to remember about reactive expressions:
Reactive expressions are lazy and only execute when called by something else. This is different from observers, which execute immediately any time their dependencies change.
Reactive expression results are cached. As long as their dependencies have not changed, subsequent calls won't cause the expression to re-execute, but instead retrieve the cached value.
Based on these two points, I don't think you have a problem and your example does what you're looking for. With both checkboxes ticked, each reactive expression would only run once per action button click.
Although I can suggest removing the unnecessary if statements in the eventReactives. That would allow the user to only check runModel2 and have all its dependencies run properly. Modified example below - I also added some message(...) statements so you can see the execution flow in the R console.
library(shiny)
ui <- fluidPage(
h3('Run Stuff'),
checkboxInput("runModel1", "Model 1"),
checkboxInput("runModel2", "Model 2"),
actionButton('runAll', 'Run Models'),
verbatimTextOutput("out1"),
verbatimTextOutput("out2")
)
server <- function(input, output, session) {
baseObject <- eventReactive(input$runAll, {
message("calculating baseObject...")
result <- runif(100)
message("...baseObject done")
return(result)
})
firstObject <- eventReactive(input$runAll, {
message("calculating firstObject...")
result <- runif(100) + baseObject()
message("...firstObject done")
return(result)
})
secondObject <- eventReactive(input$runAll, {
message("calculating secondObject...")
result <- runif(100) + firstObject()
message("...secondObject done")
return(result)
})
output$out1 <- renderPrint({
if (input$runModel1)
firstObject()
})
output$out2 <- renderPrint({
if (input$runModel2)
secondObject()
})
}
shinyApp(ui, server)
I'm having trouble creating a sequence of events in a Shiny app. I know there are other ways of handling parts of this issue (with JS), and also different Shiny functions I could use to a similar end (e.g. withProgress), but I'd like to understand how to make this work with reactivity.
The flow I hope to achieve is as follows:
1) user clicks action button, which causes A) a time-consuming calculation to begin and B) a simple statement to print to the UI letting the user know the calculation has begun
2) once calculation returns a value, trigger another update to the previous text output alerting the user the calculation is complete
I've experimented with using the action button to update the text value, and setting an observer on that value to begin the calculation (so that 1B runs before 1A), to ensure that the message isn't only displayed in the UI once the calculation is complete, but haven't gotten anything to work. Here is my latest attempt:
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
actionButton("run", "Pull Data")
mainPanel(
textOutput("status")
)
)
)
server <- function(input, output, session) {
# slow function for demonstration purposes...
test.function <- function() {
for(i in seq(5)) {
print(i)
Sys.sleep(i)
}
data.frame(a=c(1,2,3))
}
report <- reactiveValues(
status = NULL,
data = NULL
)
observeEvent(input$run, {
report$status <- "Pulling data..."
})
observeEvent(report$status == "Pulling data...", {
report$data <- test.function()
})
observeEvent(is.data.frame(report$data), {
report$status <- "Data pull complete"
}
)
observe({
output$status <- renderText({report$status})
})
}
Eventually, I hope to build this into a longer cycle of calculation + user input, so I'm hoping to find a good pattern of observers + reactive elements to handle this kind of ongoing interaction. Any help is appreciated!
I'm writing a Shinyapp that enables users, among other things, to input new entries to a mongodb and delete specific rows from it.
I'm trying to add a functionality that would allow to undo the last delete by saving a temporary copy of the row. It seems to work fine, but after I use undo, for some reason the delete button doesn't work anymore, and I can't figure out why.
I thought maybe it has something to do with the fact that there's a few other places where I use observers for the two buttons, but I don't understand why that would cause any problem (and I need them for the app to function properly) - at any rate, they don't prevent me from deleting several rows one after the other so long as I don't use the undo function.
As you can see from the code below, I've put a bunch of print() functions throughout it to try and figure out where it's going. The weird thing - none of them show up! It's like the delete button simply doesn't activate the script once undo was used. Any ideas why?
UPDATE: Here's a short version of server.R and ui.R that reproduces the problem (without using mongodb):
server.R
tempEntry<-NULL
shinyServer(function(input, output, session) {
dat<-data.frame(nums=1:3,ltrs=c("a","b","c"))
## Action: Delete entry
output$delError<-renderText({
input$delButton
isolate({if (!is.na(input$delNum)) {
tempEntry<<-dat[input$delNum,]
output$undo<<-renderUI({
actionLink("undo","Undo last delete")
})
dat<<-dat[-input$delNum,]
print("deleted")
print(dat)
} else print("nope2")
})
})
## Action: Undo delete
output$undoError<-renderText({
input$undo
if (!is.null(input$undo)) {
if (input$undo>0) {
isolate({if (!is.null(tempEntry)) {
dat<<-rbind(dat,tempEntry)
tempEntry<<-NULL
output$delError<<-renderText({""})
print(dat)
} else print("nope3")
}) } else print("undo==0") } else print("undo null")
})
})
ui.R:
library(shiny)
shinyUI(navbarPage("example",
tabPanel("moo",
titlePanel(""),
fluidPage(numericInput("delNum","Row to delete",value=NULL),
actionButton("delButton","Delete row"),
uiOutput("undo"),
div(p(textOutput("delError")),style="color:red"),
div(p(textOutput("undoError")),style="color:blue")
))))
(This also gives an error "argument 1 (type 'list') cannot be handled by 'cat'" after deleting a row, I don't know why... But the problem doesn't seem to be related to that).
Thanks!
That happens because of the output$delError<<-renderText({""}) code that overwrites the original output$delError expression by the empty one, so no surprise output$delError does not trigger on input$delButton any more.
[UPDATE]
The OP's application uses actionButton and actionLink to delete and undelete records from a database, respectively. The 'delete' button is supposed to trigger the delError expression that deletes the record and shows the outcome of deletion (e.g. 'record deleted'). Similarly, the 'undelete' button triggers the undoError expression that puts the record back into the table and reports an outcome of undeletion (e.g. 'record undeleted'). The problem is that undoError has to get rid of the output produced by delError because outputs 'record deleted' and 'record undeleted' don't make much sense when they appear together, but the output 'record deleted' can be removed only by the delError expression.
It seems that this problem can be resolved by modifying delError to make it hide its output when the 'undelete' button (or link) is pressed. But in this case, delError would trigger on both 'delete' and 'undelete' buttons without being able to say which button caused the evaluation, so it would try to delete a record when the 'undelete' button is pressed!
The sample application below provides a way to address this problem by using a global variable that stores the status of the last operation. This status is generated by two high-priority observers (one for 'delete' and another for 'undelete'), which also take care of actual deleting/undeleting of the record. The observers don't produce output that directly goes to the web page, so there is no hassle with getting rid of the messages produced by the other observer. Instead, the status variable is shown by a simple reactive expression.
server.R
tempEntry<-NULL
dat<-data.frame(nums=1:3,ltrs=c("a","b","c"))
shinyServer(function(input, output, session) {
del.status <- NULL
##################
### Observers ####
##################
delete.row <- observe({
if (input$delButton ==0 ) return() # we don't want to delete anything at start
delNum <- isolate( input$delNum ) # this is the only thing that needs to be isolated
if (is.na(delNum)) {
print('nope2')
return()
}
tempEntry <<- dat[delNum,]
dat <<- dat[-delNum,]
output$undo <<- renderUI( actionLink("undo","Undo last delete") )
del.status <<- 'deleted'
},priority=100) # make sure that del.status will be updated *before* the evaluation of output$delError
undelete.row <- observe({
if (is.null(input$undo) || input$undo==0) return() # trigger on undowe don't want to undelete anything at the beginning of the script
dat <<- rbind(dat,tempEntry)
tempEntry <<- NULL
output$undo <<- renderUI("")
del.status <<- 'undeleted'
},priority=100)
##################
### Renderers ####
##################
output$delError <- renderText({
if (input$delButton == 0) return() # show nothing until first deletion
input$undo # trigger on undo
return(del.status)
})
output$show.table <- renderTable({
input$delButton; input$undo # trigger on delete/undelete buttons
return(dat)
})
})
ui.R
library(shiny)
shinyUI(
navbarPage(
"example"
, tabPanel("moo"
, titlePanel("")
, fluidPage(
numericInput("delNum","Row to delete",value=NULL)
, div(p(textOutput("delError")),style="color:red")
, actionButton("delButton","Delete row")
, uiOutput("undo")
, tableOutput('show.table')
)
)
)
)
Note : I have read almost all the discussions on this object in shiny googlegroups and here in SO.
I need an indicator that shows the shiny server is busy. I have tried shiny-incubator, however, the problem is that I can't set a max for progress bar.
I don't want something like this : https://shiny.rstudio.com/gallery/progress-bar-example.html
What I need is something that:
shows a busy indicator message and bar (i.e just a simple animated bar, do not need to show a filling bar) as long as the server is calculating
it is shown in no matter which tab you are viewing. (not only in the related tab, but on top of the tabset)
Update 2018: Currently there is a great package to help you display loaders: shinycssloaders (source: https://github.com/andrewsali/shinycssloaders)
I've been looking for this as well. Most people suggest a conditional panel like so:
conditionalPanel(
condition="!($('html').hasClass('shiny-busy'))",
img(src="images/busy.gif")
)
You could always give yourself more control and create the conditional handling (maybe depending on more stuff) like this in your ui.R:
div(class = "busy",
p("Calculation in progress.."),
img(src="images/busy.gif")
)
where some JavaScript handles the showing and hiding of that div:
setInterval(function(){
if ($('html').attr('class')=='shiny-busy') {
$('div.busy').show()
} else {
$('div.busy').hide()
}
},100)
with some extra css you could make sure your animated busy image gets a fixed postion where it will always be visible.
In any of the above cases i found that the "shiny-busy" condition is somewhat imprecise and unreliable: the div shows for a split second and disappears while computations are still going on...
I found a dirty solution to fix that problem, at least in my apps. Feel free to try it out and maybe someone could give an insight to how and why this solves the issue.
In your server.R you'll need to add two reactiveValues:
shinyServer(function(input, output, session) {
# Reactive Value to reset UI, see render functions for more documentation
uiState <- reactiveValues()
uiState$readyFlag <- 0
uiState$readyCheck <- 0
then, in your renderPlot function (or other output function where computations go on), you use these reactive values to reset the function:
output$plot<- renderPlot({
if (is.null(input$file)){
return()
}
if(input$get == 0){
return()
}
uiState$readyFlag
# DIRTY HACK:
# Everytime "Get Plot" is clicked we get into this function
# In order for the ui to be able show the 'busy' indicator we
# somehow need to abort this function and then of course seamlessly
# call it again.
# We do this by using a reactive value keeping track of the ui State:
# renderPlot is depending on 'readyFlag': if that gets changed somehow
# the reactive programming model will call renderPlot
# If readyFlag equals readyCheck we exit the function (= ui reset) but in the
# meantime we change the readyFlag, so the renderHeatMap function will
# immediatly be called again. At the end of the function we make sure
# readyCheck gets the same value so we are back to the original state
isolate({
if (uiState$readyFlag == uiState$readyCheck) {
uiState$readyFlag <- uiState$readyFlag+1
return(NULL)
}
})
isolate({plot <- ...})
# Here we make sure readyCheck equals readyFlag once again
uiState$readyCheck <- uiState$readyFlag
return(plot)
})
Alternatively, you can use shinycssloaders package https://github.com/andrewsali/shinycssloaders
library(shiny)
library(dplyr)
library(shinycssloaders)
ui <- fluidPage(
actionButton("plot","plot"),
plotOutput("Test") %>% withSpinner(color="#0dc5c1")
)
server <- function(input, output, session) {
data <- eventReactive(input$plot,{
rnorm(1:100000)
})
output$Test <- renderPlot({
plot(data())
})
}
shinyApp(ui = ui, server = server)
Using waiter
library(shiny)
library(waiter)
ui <- fluidPage(
use_waiter(),
actionButton("plot","plot"),
plotOutput("Test")
)
server <- function(input, output, session) {
w <- Waiter$new(id = "Test")
data <- eventReactive(input$plot,{
w$show()
rnorm(1:100000)
})
output$Test <- renderPlot({
plot(data())
})
}
shinyApp(ui = ui, server = server)
I found using fadeIn() as opposed to show() helps mitigate this blinking occurence:
setInterval(function(){
if ($('html').attr('class')=='shiny-busy') {
setTimeoutConst = setTimeout(function(){
$('#loading-page').fadeIn(500);
}, delay);
} else {
clearTimeout(setTimeoutConst );
$('#loading-page').hide();
}
},10)
The busy div also appears for split seconds for the latest versions of shiny, even though no apparent calculations are going on (it was not an issue in older versions). Shiny seems to be regularly in its busy-mode for a short time. As a solution (complementing the above discussion), one can include another 2nd delayed validation of the shiny-busy html class for the conditional handling. The JavaScript-part would look something like that (example also includes check for two different div.busy-states depending on the reactive textit):
if( ($('html').attr('class')=='shiny-busy') ){
setTimeout(function() {
if ($('html').attr('class')=='shiny-busy') {
if($('#textit').html()!='Waiting...' ){
$('div.busy1').show()
}
if($('#textit').html()=='Waiting...'){
$('div.busy2').show()
}
}
},1000)
} else {
$('div.busy1').hide()
$('div.busy2').hide()
}
},100)