Updating inputs
observeEvent() and updateSelectInput()
In Your app: explore a dataset the X and Y menus were created by the server with renderUI(). There is another way: the menus exist in ui.R from the start, empty, and the server updates their choices when the data are loaded.
Try it: load a dataset and look at how the X and Y menus change.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 700
## file: ui.R
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
actionButton("load", "Load dataset"),
hr(),
# the menus exist from the start, but they are empty
selectInput("x", "X variable:", choices = NULL),
selectInput("y", "Y variable:", choices = NULL),
actionButton("analyze", "Update analysis")
),
mainPanel(
plotOutput("graph"),
verbatimTextOutput("summary")
)
)
)
## file: server.R
library(shiny)
server <- function(input, output, session) { # session is needed by update*()
dataInput <- eventReactive(input$load, {
switch(input$dataset,
rock = rock,
pressure = pressure,
cars = cars)
})
# observeEvent: DO something (a side effect) when the data change.
# It returns nothing: it updates the menus that already exist in the UI
observeEvent(dataInput(), {
vars <- names(dataInput())
updateSelectInput(session, "x", choices = vars, selected = vars[1])
updateSelectInput(session, "y", choices = vars,
selected = vars[min(2, length(vars))])
})
# freeze data + chosen variables when "Update analysis" is clicked
analysisInput <- eventReactive(input$analyze, {
req(input$x, input$y)
list(data = dataInput(), x = input$x, y = input$y)
})
output$graph <- renderPlot({
obj <- analysisInput()
plot(obj$data[[obj$x]], obj$data[[obj$y]],
xlab = obj$x, ylab = obj$y, pch = 19)
})
output$summary <- renderPrint({
obj <- analysisInput()
summary(obj$data[, c(obj$x, obj$y)])
})
}
How it works
# ui.R
1selectInput("x", "X variable:", choices = NULL)- 1
- A normal menu, with no choices yet
# server.R
1server <- function(input, output, session) {
2 observeEvent(dataInput(), {
vars <- names(dataInput())
3 updateSelectInput(session, "x", choices = vars)
})
}- 1
-
sessionis needed: it tellsupdate*()which user’s app to change - 2
-
When
dataInput()changes, do something.observeEvent()returns nothing: it is used for its side effects - 3
-
Change the choices of the existing menu with ID
x
Every input has its update*() function: updateSliderInput(), updateTextInput(), updateNumericInput()…
The code
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 700
## file: ui.R
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
actionButton("load", "Load dataset"),
hr(),
# the menus exist from the start, but they are empty
selectInput("x", "X variable:", choices = NULL),
selectInput("y", "Y variable:", choices = NULL),
actionButton("analyze", "Update analysis")
),
mainPanel(
plotOutput("graph"),
verbatimTextOutput("summary")
)
)
)
## file: server.R
library(shiny)
server <- function(input, output, session) { # session is needed by update*()
dataInput <- eventReactive(input$load, {
switch(input$dataset,
rock = rock,
pressure = pressure,
cars = cars)
})
# observeEvent: DO something (a side effect) when the data change.
# It returns nothing: it updates the menus that already exist in the UI
observeEvent(dataInput(), {
vars <- names(dataInput())
updateSelectInput(session, "x", choices = vars, selected = vars[1])
updateSelectInput(session, "y", choices = vars,
selected = vars[min(2, length(vars))])
})
# freeze data + chosen variables when "Update analysis" is clicked
analysisInput <- eventReactive(input$analyze, {
req(input$x, input$y)
list(data = dataInput(), x = input$x, y = input$y)
})
output$graph <- renderPlot({
obj <- analysisInput()
plot(obj$data[[obj$x]], obj$data[[obj$y]],
xlab = obj$x, ylab = obj$y, pch = 19)
})
output$summary <- renderPrint({
obj <- analysisInput()
summary(obj$data[, c(obj$x, obj$y)])
})
}
req(): wait for what you need
In this app analysisInput() starts with req(input$x, input$y). req() checks that its arguments are “available” (not NULL, not empty, not FALSE…). If they are not, it stops the computation silently: no red error, the output simply waits.
Use it whenever an output needs something that may not be there yet: an uploaded file, a selection, a click.
eventReactive() vs observeEvent()
eventReactive() |
observeEvent() |
|
|---|---|---|
| Returns | a value, used as x() |
nothing |
| Used for | computing data for the outputs | doing things: updating inputs, showing messages, saving |
| Runs | when an output needs it (lazy) | always, at every trigger (eager) |