Are there global variables in R Shiny?
This page on the Shiny webpage explains scoping of Shiny variables.
Global variables can either be put in server.R
(as per Ricardo's answer) or in global.R
.
Objects defined in global.R are similar to those defined in server.R outside shinyServer(), with one important difference: they are also visible to the code in ui.R. This is because they are loaded into the global environment of the R session; all R code in a Shiny app is run in the global environment or a child of it.
In practice, there aren’t many times where it’s necessary to share variables between server.R and ui.R. The code in ui.R is run once, when the Shiny app is started and it generates an HTML file which is cached and sent to each web browser that connects. This may be useful for setting some shared configuration options.
How to make a local variable global in R using Shiny?
Just create the variable outside any sub-function and update its value using <<-
. This variable will be global per each session.
server <- function(input, output) {
# init variable here
algorithm_output <- NULL
out_plots <- eventReactive(input$ok_input, {
# to modify a global variable use <<- instead of <- or =
algorithm_output <<- some_global_function(3, 2, 1)
do.call("grid.arrange", c(algorithm_output$indexes, nrow=3))
})
output$indexes <- renderPlot({
out_plots()
})
out_means <- eventReactive(input$ok_means, {
k = as.integer(input$k)
# you can get access to the updated value of your variable
matplot(algorithm_output$means[[k-1]], type = "l", lty=1)
})
output$means <- renderPlot({
out_means()
})
}
How to assign global variables that can be accessed by other output$function in R shiny?
If you really want to assign something to global, you can use the <<-
operator. Or assign("x", value = data, envir = .GlobalEnv)
.
But, you really don't want to. It's almost never, ever a good idea. Especially since you need to make use of reactive inputs.
If sample_d <- data()
is too many keystrokes, just ignore assigning within the local environment (the output
object) and use data()
.
output$plot <- renderPlot({
plot(density(data()[,1]), main = input$title)
})
If you want an object with multiple values, a list of objects, make your reactive object into a list and reference with $
Be sure to note however, your reactive object is a function call. So if the reactive object is data
and we want the list item normalized_df
we would call the reactive object data()
with ()
and then index into the list that function returns with $
. So all together we would have data()$normalized_df
.
data <- reactive({
req(input$filedata)
d <- read.csv(input$filedata$datapath, header = T)
list(data = d,
normalized_df = d + 1.5,
advanced_normalized = df + 1.5 * 45.454)
})
output$normalized_plot <- renderPlot({ #Does not work
plot(density(data()$normalizd_df[,1]), main = input$title)
})
output$advanced_normalized_plot <- renderPlot({
plot(density(data()$advanced_normalized[,1]), main = input$title)
})
Below is a minimal example that shows this in action:
library(shiny)
ui <- fluidPage(
titlePanel("Old Faithful Geyser Data"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 10,
value = 2)
),
mainPanel(
column(6, verbatimTextOutput("console1")),
column(6, verbatimTextOutput("console2"))
)
)
)
server <- function(input, output) {
data1 <- reactive({
datum <- mtcars
d1 <- mtcars[1:input$bins]
d2 <- d1 + 1
d3 <- d1 * 1.5
list(d1, d2, d3)
})
data2 <- reactive({
datum <- mtcars
list(d1 = mtcars[1:input$bins],
d2 = mtcars[1:input$bins] + 1,
d3 = mtcars[1:input$bins] * 1.5)
})
output$console1 <- renderPrint({
data1()
})
output$console2 <- renderPrint({
data2()
})
}
# Run the application
shinyApp(ui = ui, server = server)
How to use reactive global variable in global.R in R Shiny
I agree with @JohnPaul and @Lee88, your catPath
probably belongs within server.R
. Having said that, I'll keep it here for now (assuming you have other reasons for this in your MWE).
global.R
:
catPath <- ""
I needed to set it to something in order to be reference-able later, otherwise the value used here should be meaningless (though it will be returned if no action taken).
ui.R
:
I added a "stop?" action button so that you can "exit" your app and capture the value of catPath
into the calling environment. Not required if you don't intend to intentionally exit the app.
fluidPage(
titlePanel("Using global variable"),
fluidRow(
uiOutput("ui1"),
uiOutput("ui2"),
uiOutput("ui3"),
actionButton("stopme", "Stop?")
)
)
server.R
:
I changed output$ui3
to create the HTML object (not perform calculations), and then observe two events and act on them. Again, if you don't need the "stop?" button above, you probably don't need the second observe here. (If you do use it, note that the argument to stopApp
is returned invisibly to the caller.)
function(input, output, session) {
output$ui1 <- renderUI({
selectInput("dataset", "firstValue", choices = c("first", "second", "third"))
})
output$ui2 <- renderUI({
actionButton("doIt", class="btn-primary", "change")
})
output$ui3 <- renderUI({
textInput("myinput", "catPath", "")
})
observeEvent(input$doIt, {
catPath <<- paste(input$dataset, "/completed", sep = "")
updateTextInput(session, inputId = "myinput", value = catPath)
})
observeEvent(input$stopme, { stopApp(catPath); })
}
Doing something like newCatPath <- runApp("path/to/dir")
.
Assign to global shiny environment in package
You can setup the data using the global.R
file. Any R objects that are created in the global.R file become available to the app.R file, or the ui.R and server.R files respectively. Take a look at how to modularize shiny apps.
EDIT: As pointed out in the comments, you can use local = TRUE
to only load objects into the shiny environment.
R Shiny: global variable for all functions in server.R
The idea to use reactive is the right direction; however you didn't do it quite right. I just added one line and it is working:
shinyServer(function(input, output, session) {
df <- NULL
in_data <- reactive({
inFile <- input$file1
if (is.null(inFile)) return(NULL)
else df <<- read.csv(inFile$datapath, as.is=TRUE)
return(NULL)
})
output$frame <- renderTable({
call.me = in_data() ## YOU JUST ADD THIS LINE.
df
})
})
Why? Because a reactive object is very similar to a function, which gets executed only when you call it. Thus the 'standard' way of your code should be:
shinyServer(function(input, output, session) {
in_data <- reactive({
inFile <- input$file1
if (is.null(inFile)) return(NULL)
else read.csv(inFile$datapath, as.is=TRUE)
})
output$frame <- renderTable({
in_data()
})
})
Related Topics
R - Faster Way to Calculate Rolling Statistics Over a Variable Interval
Reading a CSV File Organized Horizontally
Finding Where Two Linear Fits Intersect in R
Update a Column of Nas in One Data Table with the Value from a Column in Another Data Table
Dealing with Spaces and "Weird" Characters in Column Names with Dplyr::Rename()
List Members Can Be Accessed with Partial Name? Is This a Feature
Trying to Merge Multiple CSV Files in R
How to Start a for Loop in R Programming
Fitting a Curve to Specific Data
Ggplot2: Splitting Facet/Strip Text into Two Lines
How to Change the Format of an Individual Facet_Wrap Panel
Add an Image to a Table-Like Output in R
How to Get the Nth Element of Each Item of a List, Which Is Itself a Vector of Unknown Length