I have a flexdashboard that uses shiny. Here's a MRE repo and gist of the .Rmd. When I put the app live on shinyapps.io, I realized that one user's actions could affect other users. I understand that this is a scoping issue, but I'm confused about how scoping works in Flexdashboard.
This page explains scoping for 'regular' shiny apps:
You might want some objects to be visible across all sessions. For example, if you have large data structures, or if you have utility functions that are not reactive (ones that don’t involve the input or output objects), then you can create these objects once and share them across all user sessions (within the same R process), by placing them in app.R , but outside of the server function definition.
In Flexdashboard, there is no app.R file or server function. How does scoping work in these types of shiny apps?
I have several eventReactive() functions like this that get updated for User 2 when User 1 hits submit and triggers observeEvent(input$submit, {}) at the end of the file.
eventReactive(rv$run2, {
if (remote==1) {
master$df <- drop_read_csv("/dash/master.csv", stringsAsFactors = FALSE)
} else {
master$df <- read.csv("dash/master.csv", stringsAsFactors = FALSE)
}
}, ignoreNULL = TRUE)
I originally posted this to RStudio Community about 9 hours ago, but it did not generate many views (<20) or any discussion.
Moving rv <- reactiveValues() out of the global chunk did the trick.
Related
Introduction
I have created an R shiny dashboard app that is quickly getting quite complex. I have over 1300 lines of code all sitting in app.R and it works. I'm using RStudio.
My application has a sidebar and tabs and rather than using modules I dynamically grab the siderbar and tab IDs to generate a unique identifier when plotting graphs etc.
I'm trying to reorganise it to be more manageable and split it into tasks for other programmers but I'm running into errors.
Working Code
My original code has a number of library statements and sets the working directory to the code location.
rm(list = ls())
setwd(dirname(rstudioapi::getActiveDocumentContext()$path))
getwd()
I then have a range of functions that sit outside the ui/server functions so are only loaded once (not reactive). These are all called from within the server by setting the reactive values and calling the functions from within something like a renderPlot. Some of them are nested, so a function in server calls a function just in regular app.R which in turn calls another one. Eg.
# Start of month calculation
som <- function(x) {
toReturn <- as.Date(format(x, "%Y-%m-01"))
return(toReturn)
}
start_fc <- function(){
fc_start_date <- som(today())
return(fc_start_date)
}
then in server something like this (code incomplete)
server <- function(input, output, session) {
RV <- reactiveValues()
observe({
RV$selection <- input[[input$sidebar]]
# cat("Selected:",RV$selection,"\r")
})
.......
cat(paste0("modelType: ",input[[paste0(RV$selection,"-modeltype")]]," \n"))
vline1 <- decimal_date(start_pred(input[[paste0(RV$selection,"-modeltype")]],input[[paste0(RV$selection,"-modelrange")]][1]))
vline2 <- decimal_date(start_fc())
.......
Problem Code
So now when I take all my functions and put them into different .R files I get errors indicating the functions haven't been loaded. If I load the source files by highlighting them and Alt-Enter running them so they are loaded into memory then click on Run App the code works. But if I rely on Run App to load those source files, and the functions within them, the functions can't be found.
source('./functionsGeneral.R')
source('./functionsQuote.R')
source('./functionsNewBusiness.R')
source('./ui.R')
source('./server.R')
shinyApp(ui, server)
where ui.R is
source('./header.R')
source('./sidebar.R')
source('./body.R')
source('./functionsUI.R')
ui <- dashboardPage(
header,
sidebar,
body
)
Finally the questions
In what order does R Shiny Dashboard run the code. Why does it fail when I put the exact same inline code into another file and reference it with source('./functions.R')? Does it not load into memory during a shiny app session? What am I missing?
Any help on this would be greatly appreciated.
Thanks,
Travis
Ok I've discovered the easiest way is to create a subfolder called R and to place the preload code into that folder. From shiny version 1.5 all this code in the R folder is loaded first automatically.
If I call a package, like library(tidyverse), in global.R it adds approx. 2 seconds to the initial launch of app on shinyapps.io. Note that this appears to be the case if no one has used the app recently (but not when there are concurrent users).
I don't need that package until an observeEvent is executed.
I can put the library(tidyverse) inside the observeEvent, but then that takes 2 seconds when time is of essence.
Can I load libraries just after the UI finishes loading but before the observeEvent?
Something like: if shiny app is idle (flushed?), then load a package?
Below is one way to do it that relies on onFlushed, but I'd like to see if someone finds something better (like using invalidatelater as mentioned in the
comments above).
I'm not sure what onFlushed does, but here's what the documentation says:
"onFlushed registers a function that will be called after Shiny flushes the reactive system."
I think that a flush means that all the other reactives are "resolved." In my case, the UI appears far quicker and allows user input BEFORE tidyverse is loaded, which makes the app more responsive to begin with.
Put the below in the server:
values <- reactiveValues(finished.init = FALSE)
session$onFlushed(function() {
values$finished.init <- TRUE
})
observe({
req(values$finished.init)
library(tidyverse)
#any other libraries you'd like to load after initial flush
})
My current workflow in a shiny application is to run a R script as a cron job periodically to pull various tables from multiple databases as well as download data from some APIs. These are then saved as a .Rdata file in a folder called data.
In my global.R file I load the data by using load("data/workingdata.Rdata"). This results in all the dataframes (about 30) loading into the environment. I know I can use the reactiveFileReader() function to refresh the data, but obviously it would have to be used in the server.R file because of an associated session with the function. Also, I am not sure if load is accepted as a readFunc in reactiveFileReader(). What should be the best strategy for the scenario here?
This example uses a reactiveVal object with observe and invalidateLater. The data is loaded into a new environment and assigned to the reactiveVal every 2 seconds.
library(shiny)
ui <- fluidPage(
actionButton("generate", "Click to generate an Rdata file"),
tableOutput("table")
)
server <- shinyServer(function(input, output, session) {
## Use reactiveVal with observe/invalidateLater to load Rdata
data <- reactiveVal(value = NULL)
observe({
invalidateLater(2000, session)
n <- new.env()
print("load data")
env <- load("workingdata.Rdata", envir = n)
data(n[[names(n)]])
})
## Click the button to generate a new random data frame and write to file
observeEvent(input$generate, {
sample_dataframe <- iris[sample(1:nrow(iris), 10, F),]
save(sample_dataframe, file="workingdata.Rdata")
rm(sample_dataframe)
})
## Table output
output$table <- renderTable({
req(data())
data()
})
})
shinyApp(ui = ui, server = server)
A few thoughts on your workflow:
In the end with your RData-approach you are setting up another data source in parallel to your databases / APIs.
When working with files there always is some housekeeping-overhead (e.g. is your .RData file completed when reading it?). In my eyes this (partly) is what DBMS are made for – taking care about the housekeeping. Most of them have sophisticated solutions to ensure that you get what you query very fast; so why reinvent the wheel?
Instead of continuously creating your .RData files and polling data with the reactiveFileReader() function you could directly query the DB for changes using reactivePoll (see this
for an example using sqlite). If your queries are long running (which I guess is the cause for your workflow) you can wrap them in a future and run them asynchronously (see this post
to get some inspiration).
Alternatively many DBMS provide something like materialized views to avoid long waiting times (according user privileges presumed).
Of course, all of this is based on assumptions, due to the fact, that your eco-system isn’t known to me, but in my experience reducing interfaces means reducing sources of error.
You could use load("data/workingdata.Rdata") at the top of server.R. Then, anytime anyone starts a new Shiny session, the data would be the most recent. The possible downsides are that:
there could be a hiccup if the data is being written at the same time a new Shiny session is loading data.
data will be stale if a session is open just before and then after new data is available.
I imagine the first possible problem wouldn't arise enough to be a problem. The second possible problem is more likely to occur, but unless you are in a super critical situation, I can't see it being a substantial enough problem to worry about.
Does that work for you?
There are resources on how to create a Minimal, Complete, and Verifiable example in general on Stack Overflow, and on how to make a great R reproducible example. However, there are no similar guidelines for shiny questions, while adhering to certain standards makes it much more likely that quality answers will be given, and thus that your question will be resolved.
However, asking a good Shiny question can be difficult. shiny apps are often large and complex, use multiple data sources, and the code is often split over multiple files, making it difficult to share easily reproducible code with others. Even though a problem may be caused in server.R, the example is not reproducible without the contents of ui.R (and possibly other files like stylesheets or global.R). Copy-pasting the contents of all these files individually is cumbersome, and requires other users to recreate the same file structure to be able to reproduce the problem.
So; how to convert your shiny app into a good reproducible example?
Example data
Of course, all guidelines regarding sample data mentioned in the answer on the question “how to make a great R reproducible example” also hold when creating questions related to Shiny. To summarize: Make sure no additional files are needed to run your code. Use sample datasets like mtcars, or create some sample data with data.frame(). If your data is very complex and that complexity is really required to illustrate the issue, you could also use dput(). Avoid using functions like read.csv(), unless of course you have questions related to functions like fileInput.
Example code
Always reduce your code to the bare minimum to reproduce your error or unexpected behavior. This includes removing calls to additional .CSS files and .js files and removing unnecessary functions in the ui and the server.
Shiny apps often consist of two or three files (ui.R, server.R and possibly global.R), for example this demo application. However, it is preferable to post your code as a single script, so it can easily be run by others without having to manually create those files. This can easily be done by:
wrapping your ui with ui <- fluidPage(…),
the server with server <- function(input,output, session) {…},
and subsequently calling shinyApp(ui, server).
So a simple skeleton to start with could look as follows:
library(shiny)
ui <- fluidPage(
)
server <- function(input,output,session) {
}
shinyApp(ui, server)
Working Example
So, taking all the above into account, a good Minimal, Complete, and Verifiable example for a Shiny application could look as follows:
library(shiny)
df <- data.frame(id = letters[1:10], value = seq(1,10))
ui <- fluidPage(
sliderInput('nrow', 'Number of rows', min = 1, max = 10, value = 5),
dataTableOutput('my_table')
)
server <- function(input, output, session) {
output$my_table <- renderDataTable({
df[1:input$nrow,]
})
}
shinyApp(ui, server)
Adding CSS
There are multiple ways to add custom CSS to a Shiny application, as explained here. The preferred way to add CSS to a Shiny application in a reproducible example is to add the CSS in the code, rather than in a separate file. This can be done by adding a line in the ui of an application, for example as follows:
tags$head(tags$style(HTML('body {background-color: lightblue;}'))),
I would like to run R code in two locations, in an Rnw file and as an interactive shiny R markdown document.
Thus, what I need, since interactive shiny components do not work in Rnw files, is a code snippet in R that detects whether to load the interactive code or not.
This seems to work, but it feels like a quick hack:
if (exists("input")) { # input is provided by shiny
# interactive components like renderPlot for shiny
} else {
# non-interactive code for Rnw file
}
Is there a stable solution or something like a global variable that I can access that says whether shiny is running at the moment? Or should I check whether the shiny package is loaded?
What's safest?
There is now a function shiny::isRunning().
This information is provided directly via Shiny’s isRunning function.
Outdated answer below:
You can do the following:
shiny_running = function () {
# Look for `runApp` call somewhere in the call stack.
frames = sys.frames()
calls = lapply(sys.calls(), `[[`, 1)
call_name = function (call)
if (is.function(call)) '<closure>' else deparse(call)
call_names = vapply(calls, call_name, character(1))
target_call = grep('^runApp$', call_names)
if (length(target_call) == 0)
return(FALSE)
# Found a function called `runApp`, verify that it’s Shiny’s.
target_frame = frames[[target_call]]
namespace_frame = parent.env(target_frame)
isNamespace(namespace_frame) && environmentName(namespace_frame) == 'shiny'
}
Now you can simply use shiny_running() in code and get a logical value back that indicates whether the document is run as a Shiny app.
This is probably (close to) the best way, according to a discussion on Shiny the mailing list — but do note the caveats mentioned in the discussion.
Adapted from code in the “modules” package.
Alternatively, the following works. It may be better suited for the Shiny/RMarkdown use-case, but requires the existence of the YAML front matter: It works by reading the runtime value from that.
shiny_running = function ()
identical(rmarkdown::metadata$runtime, 'shiny')
Update: After Konrad Rudolphs comment I rethought my approach. My original answer can be found down below.
My approach is different from Konrad Rudolphs and maybe different to the OPs initial thoughts. The code speaks for itself:
if (identical(rmarkdown::metadata$runtime, "shiny")) {
"shinyApp"
} else {
"static part"
}
I would not run this code from inside an app but use it as a wrapper around the app. If the code resides within a .Rmd with runtime: shiny in the YAML front matter it will start the app, if not, it will show the static part.
I guess that should do what you wanted, and be as stable as it could get.
My original thought would have been to hard code whether or not you were in an interactive document:
document_is_interactive <- TRUE
if (document_is_interactive) {
# interactive components like renderPlot for shiny
} else {
# non-interactive code for Rnw file
}
Although possible, this could lead to problems and would therefore be less stable than other the approach with rmarkdown::metadata$runtime.