Search code examples
rdatatableshinyinteractiveselectall

Implement 'Select All' option in reactive shiny


I'm building a shiny app which displays a table based on user selections. I have a table called 'stat' and it is of the form

Team      Season   wins  loss  draws
Arsenal   1992-93   18    12    14
Arsenal   1993-94   21    10    7 
Liverpool 1992-93   22    6     12
Liverpool 1993-94   19    13    10
 All        All     0     0     0

I need to filter the data based on the team and season selected. The table should display all season's data when 'All' is selected for season and vice versa for team.

I had tried some of the code below but it's not working as expected.

#UI part
selectInput("club", "Select Club", choices = stat$Team),
                 selectInput("season", "Select Season", choices = 
 stat$Season)

#server part
server <- output$statdata <- renderTable({
  teamfilter <- subset(stat, (stat$Team == input$club) & 
                         (stat$Season==input$season))
})

  observe({
    if("Select All" %in% input$club)
      selected_choices = stat$Team[-1]
    else
      selected_choices = input$club
    updateSelectInput(session, "club", selected = selected_choices)

})

Can some suggest me a correct code or tell me if any changes are required to make the code working.


Solution

  • Here's a full-working example showing how one might incorporate "All" into a selectInput:

    library(shiny)
    dat <- mtcars
    app <- shinyApp(
      ui = shinyUI(
        pageWithSidebar(
          headerPanel("Simple Test"),
          sidebarPanel(
            selectInput("cyl", "Cylinders", choices = c("All", sort(unique(dat$cyl)))),
            selectInput("gear", "Gears", choices = c("All", sort(unique(dat$gear))))
          ),
          mainPanel(
            shiny::tableOutput("out")
          )
        )
      ),
      server = function(input, output, session) {
        filtered <- reactive({
          rows <- (input$cyl == "All" | dat$cyl == input$cyl) &
            (input$gear == "All" | dat$gear == input$gear)
          dat[rows,,drop = FALSE]
        })
        output$out <- renderTable(filtered())
      }
    )
    

    This takes advantage of R's recycling: while input$gear == "All" returns a single logical, dat$gear == input$gear returns a vector as long as the number of rows in the table. Because the first logical is length 1, it is recycled as long as the second vector. This means that input$gear == "All" is effectively a vector of length "nrow(dat)", and is always all TRUE or all FALSE. When true, none of the other $gear comparisons matter (I'm glossing over NA comparisons); when false, the other $gear comparisons are relevant.

    Demonstration of recycling:

    1 == 1
    # [1] TRUE
    1 == 2
    # [1] FALSE
    
    1 == 1:5
    # [1]  TRUE FALSE FALSE FALSE FALSE
    
    1 == 1 | 1 == 1:5
    # [1] TRUE TRUE TRUE TRUE TRUE
    1 == 2 | 1 == 1:5
    # [1]  TRUE FALSE FALSE FALSE FALSE