Need to have input change reflected in several outputs and controls


#1

Hello,

I have an application that maintains three tabs. One of the tabs has 12 selectInput controls and 2 plotly plots. When the first selectInput changes, it updates the rest of the controls and generates a plot. When one or several or all of the controls change, their values are reflected on the two plots of the tab. My problem is that when an input fires, its change is reflected only in one scenario and ignores the rest. I tried to track down the bug using:

options(shiny.reactlog=TRUE)

and launched the reactive log visualization to find that each output or update scenario is triggered alone. Here is a small code example that reflects my design but does not reproduce the issue.

server.R

library(shiny)
library(plotly)
library(dplyr)

shinyServer(function(input, output,session) {

  output$first <- renderPlotly({

    # generate bins based on input$bins from ui.R
    cars.subset <- cars[1:input$cars,] %>% 
      filter(speed >= input$speedRange[1] & speed <= input$speedRange[2]) %>%
      filter(dist >= input$distanceRange[1] & dist <= input$distanceRange[2])
      
    plot_ly(cars.subset, x=~speed, y=~dist, type="scatter")
  })
  
  output$second <- renderPlotly({
    cars.subset <- cars[1:input$cars,] %>% 
      filter(speed >= input$speedRange[1] & speed <= input$speedRange[2]) %>%
      filter(dist >= input$distanceRange[1] & dist <= input$distanceRange[2])
    plot_ly(cars.subset,x=~dist, y=~speed, type="box")
  })
  
  observeEvent(input$cars,{
    updateSliderInput(session,"display",value = input$cars)
    cars.subset <- cars[1:input$cars,]
    min.speed <- min(cars.subset[,1])
    max.speed <- max(cars.subset[,1])
    min.distance <- min(cars.subset[,2])
    max.distance <- max(cars.subset[,2])
    updateSliderInput(session, "speedRange", value = c(min.speed,max.speed))
    updateSliderInput(session, "distanceRange", value=c(min.distance,max.distance))
  })

})

ui.R

library(shiny)
library(plotly)

shinyUI(fluidPage(

  # Application title
  titlePanel("Old Faithful Geyser Data"),

  # Sidebar with a slider input for number of bins
  sidebarLayout(
    sidebarPanel(
      
      sliderInput("cars",
                  "Number of cars:",
                  min = 1,
                  max = 50,
                  value = 25),
      sliderInput("display",
                  "Number of cars chosen:",
                  min = 1,
                  max = 50, 
                  value = 1),
      sliderInput("speedRange",
                  "Speed Range",
                  min = 4,
                  max = 25,
                  dragRange =  TRUE,
                  value = c(4,25)),
      sliderInput("distanceRange",
                  "Distance Range",
                  min = 1,
                  max = 120,
                  dragRange = TRUE,
                  value = c(1,120))
    ),

    # Show a plot of the generated distribution
    mainPanel(
      plotlyOutput("first"),
      plotlyOutput("second")
    )
  )
))

Any suggestions on how to handle such problems? And how to make sure that an input change reflects in all outputs or updates that are dependent on it?


#2

If I interpreted the intent of the app correctly, I was able to spot a couple potential issues with the code:

  1. updateSliderInput updates the value but not the min and max of the sliders.
  2. The data that should be used to determine the parameters for the speed and distance sliders should be different than the data used for plotting.

However I wasn’t sure what input$display is supposed to control, so for now I commented it out. I hope this helps!

library(shiny)
library(plotly)
library(dplyr)

ui <- fluidPage(
  
  # Application title
  titlePanel("Cars Data"),
  
  # Sidebar with a slider inputs
  sidebarLayout(
    sidebarPanel(
      
      HTML("There are 50 cars in the dataset. The slider below selects the first n
           cars to pass on to the following sliders."),
      br(), br(),
      
      sliderInput("cars",
                  "Number of cars:",
                  min = 1,
                  max = 50,
                  value = 25),
      hr(),
      
      #sliderInput("display",
      #            "Number of cars chosen:",
      #            min = 1,
      #            max = 50, 
      #            value = 1),
      sliderInput("speedRange",
                  "Speed Range",
                  min = 4,
                  max = 25,
                  dragRange =  TRUE,
                  value = c(4,25)),
      sliderInput("distanceRange",
                  "Distance Range",
                  min = 1,
                  max = 120,
                  dragRange = TRUE,
                  value = c(1,120))
    ),
    
    # Show plots
    mainPanel(
      plotlyOutput("first"),
      plotlyOutput("second")
    )
  )
)

server <- function(input, output,session) {
  
  # selected cars - for determining speed and distance sliders
  cars.subset <- reactive({
    cars[1:input$cars,]
  })
  
  # subsetted data frame - for plotting
  cars.to.plot <- reactive({
    cars.subset() %>%
      filter(speed >= input$speedRange[1] & speed <= input$speedRange[2]) %>%
      filter(dist >= input$distanceRange[1] & dist <= input$distanceRange[2])
  })
  
  # first plot
  output$first <- renderPlotly({
    plot_ly(cars.to.plot(), x = ~speed, y = ~dist, type = "scatter")
  })
  
  # second plot
  output$second <- renderPlotly({
    plot_ly(cars.to.plot(), x = ~dist, y = ~speed, type = "box")
  })
  
  # update UI
  observeEvent(input$cars,{
    #updateSliderInput(session, "display", value = input$cars)
    min.speed <- min(cars.subset()[,1])
    max.speed <- max(cars.subset()[,1])
    min.distance <- min(cars.subset()[,2])
    max.distance <- max(cars.subset()[,2])
    updateSliderInput(session, "speedRange", 
                      value = c(min.speed, max.speed),
                      min = min.speed, max = max.speed)
    updateSliderInput(session, "distanceRange", 
                      value = c(min.distance, max.distance),
                      min = min.distance, max = max.distance)
  })
  
}

shinyApp(ui, server)

#3

Thanks Mine!

I did realize that I have a couple of issues with my design after watching the videos in the following link:
https://www.rstudio.com/resources/videos/effective-reactive-programming/

And yes, reactive is the way to go for handling data changing according to inputs.

P.S. input$display was simply there to show that there is a control updated by change in another control.