Shiny Reactive not Invalidating

I have selectInputs that depend on the data returned from other selectInputs that are not being invalidated when upstream inputs are updated.

steps:

  1. The user will select which client they want to view.
  2. This will cause a fairly large query to kick off for that client's data.
  3. From that dataset they can further filter to which client site they want to view
  4. Which will trigger updates charts, tables, etc...

The issue arises when they change the client. This causes step 4 to momentarily error because the input to select the site doesn't invalidate and has a value that doesn't correspond to an actual site within that client.

I have used dependent filters numerous times in different apps and never had this issue.

Edit2 : I was able to get a reproducible example! I've replaced the code snippet. and removed the images as this will work.

To recreate let load. Change client an you will get this error.

Listening on http://127.0.0.1:7921
Warning: Error in order: argument 1 is not a vector
105: order
102: to_basic.GeomLine
100: layers2traces
99: gg2list
98: ggplotly.ggplot
96: "plotly":::"prepareWidget"
95: func
82: origRenderFunc
81: output$myplot
1: runApp

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

clients <- data.frame( #in reality this list is a small pull from Postgres
  client_name = c("ABC Inc", "XYZ Co")
)

data <- data.frame( #in reality this is stored in Postgres accross multiple tables.
  client_name =  c("ABC Inc", "ABC Inc", "ABC Inc", "ABC Inc", "ABC Inc", "ABC Inc", "ABC Inc", "ABC Inc","ABC Inc", "ABC Inc", "ABC Inc", "ABC Inc",
                   "XYZ Co", "XYZ Co", "XYZ Co", "XYZ Co","XYZ Co", "XYZ Co", "XYZ Co", "XYZ Co","XYZ Co", "XYZ Co", "XYZ Co", "XYZ Co"),
  facility = c("Main St", "Main St", "Main St", "Main St", "Main St", "Main St",
               "123 N Ave", "123 N Ave", "123 N Ave", "123 N Ave", "123 N Ave", "123 N Ave",
               "Oregon Tr", "Oregon Tr", "Oregon Tr", "Oregon Tr", "Oregon Tr", "Oregon Tr", 
               "Penn Ave", "Penn Ave", "Penn Ave", "Penn Ave", "Penn Ave", "Penn Ave"),
  date = lubridate::ymd(c('2019-01-01','2019-02-01', '2019-03-01','2019-01-01','2019-02-01', '2019-03-01','2019-01-01','2019-02-01', '2019-03-01','2019-01-01','2019-02-01', '2019-03-01',
                          '2019-01-01','2019-02-01', '2019-03-01','2019-01-01','2019-02-01', '2019-03-01','2019-01-01','2019-02-01', '2019-03-01','2019-01-01','2019-02-01', '2019-03-01')),
  amount = sample(10:20, 24, replace = T)
)


ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(width = 3,
                 uiOutput("choose.client"),
                 uiOutput("choose.facility")
                 ),
    mainPanel(width = 9,
              plotlyOutput("myplot")
              )
  )
)

server <- function(input, output, session){
  
  # data ----
  full.data <- reactive({ 
    # In reality this is a large pull from a database that is only filtered by the first input
    # I don't want to repull each time the "facility" is updated, just pull when the client is chosen.
    # I am just trying to make an example
    
    d <- data %>% 
      filter(client_name == input$client.choice)
    
    return(d)
  })
  
  filtered.data <- reactive({
    # in my code this is reactive applies the second filter which and is used in multiple tables and charts
    req(input$facility.choice)
    req(full.data())
    
    t <- full.data() %>% 
      filter(facility %in% input$facility.choice) %>% 
      group_by(date) %>% 
      summarise(amount = sum(amount)) %>% 
      ungroup

    return(t)
    
  })
  
  
  #sidebar inputs ----
  
  output$choose.client <- renderUI({
    # In actuality this would pull a small set from the database to get the list of clients
    selectInput("client.choice",
                label = "Choose Client",
                choices = clients$client_name,
                multiple = FALSE
                )
  }) 
  
  output$choose.facility <- renderUI({
    # This filter will depend on what gets returned in full.data
    
    req(input$client.choice)
    req(full.data())
    
    f = full.data() %>% 
      distinct(facility) %>% 
      pull(facility)
    
    selectInput("facility.choice",
                label = "Choose facility",
                choices = f,
                multiple = TRUE,
                selected = f
                )
  })
  
  # charts and tables----
  output$myplot<- renderPlotly({
    filtered.data() %>% 
      ggplot(aes(x = date,y = amount)) +
      geom_line(color = "red") + 
      theme_minimal()
      
  })
}

shinyApp(ui = ui, server = server)

Seems like the easiest way to remove the error is to add a check in renderPlotly() for the number of rows in your data. If there is no data, then return NULL (which renders an empty <div>) instead of trying to make of plot of no data:

output$myplot<- renderPlotly({
    d <- filtered.data()
    if (nrow(d) == 0) return(NULL)
    ggplot(d, aes(x = date,y = amount)) +
      geom_line(color = "red") + 
      theme_minimal()
})
1 Like

Yeah, that's basically what I ended up doing. Further testing showed that I don't get the error if I leave as a ggplot, but it does if I render as a plotly.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.