1

I'm using a filter on a shiny app to filter a dataframe. At the moment, I have to input the possible values into the ui manually. Is there a way to read the unique values from the server dataframe and populate the input options using that list? I know it is possible to do this by making the table global, but I'm trying to avoid doing this.

Example code:

ui <- fluidPage(
  sidebarPanel(
    selectInput(inputId = 'col1Input',
                label ='col1',
                choices = c(1,2,3))),
  mainPanel(
    DT::dataTableOutput("table")))
server <- function(input,output){
  df <- data.frame('col1' = c(1,2,3), 'col2' = c(1,2,3))
  output$table <- DT::renderDataTable(dplyr::filter(df, col1 == input$col1Input))
}

shinyApp(ui = ui, server = server)

Thanks!

unknown
  • 853
  • 1
  • 10
  • 23

1 Answers1

3

You could use renderUI to build the UI from the server. Then you could simply use one of the columns from the dataframe you have in the server to build your select input?

See here for how to use renderUI.

ui <- fluidPage(
  uiOutput("sidebarOutput"),
  uiOutput("mainPanel")
)

server <- function(input,output){
  df <- data.frame('col1' = c(1,2,3), 'col2' = c(1,2,3))
  output$sidebarOutput <- renderUI({
    sidebarPanel(
      selectInput(inputId = 'col1Input',
                  label =colnames(df[1]),
                  choices = df[[1]]))
  })

  output$mainPanel <- renderUI({
      output$table <- DT::renderDataTable({
          validate(
            need(input$col1Input != "", "No column selected")
          )
          dplyr::filter(df, col1 == input$col1Input)
        })

      mainPanel(
        DT::dataTableOutput("table")
      )
  })
}

shinyApp(ui = ui, server = server)

Edit: You might also want to filter on a specific column that you select. You can do this like so:

ui <- fluidPage(
  uiOutput("sidebarOutput"),
  uiOutput("mainPanel")
)

server <- function(input,output){
  df <- data.frame('col1' = c(1,2,3), 'col2' = c(4,5,6))
  output$sidebarOutput <- renderUI({
    sidebarPanel(
      selectInput(inputId = 'filtercolumn',
                  label = "Select a column to filter on",
                  choices = colnames(df)),

      renderUI({selectInput(inputId = 'valuetofilter',
                  label = paste0("filtering on column: ", colnames(df[input$filtercolumn])),
                  choices = df[[input$filtercolumn]])})
      )
  })

  output$mainPanel <- renderUI({
      output$table <- DT::renderDataTable({
          validate({
            need(input$filtercolumn != "", "No column selected")
            need(input$valuetofilter != "", "No value selected")
          })

          dplyr::filter(df,(!!as.name(input$filtercolumn)) == !!input$valuetofilter) #note the unquoting
        })

      mainPanel(
        DT::dataTableOutput("table")
      )
  })
}

shinyApp(ui = ui, server = server)

With thanks to this answer

Harvey Ellis
  • 596
  • 1
  • 3
  • 12
  • hm I can kind of see how this works. Would I use it to send the values to the UI seleectInput and then back to server to filter the data? – unknown Mar 07 '19 at 11:47
  • I've added an example based on your code. All this has really done is to move the dataframe from something that is global into the renderUI block. Let me know if this is what you mean? – Harvey Ellis Mar 07 '19 at 11:55