I have data that looks something like the data set Orange
where there are columns that might contain duplicate values, however, each row is unique.
My code:
library(shiny)
library(DT)
library(data.table)
d <- copy(Orange)
col_names <- names(Orange)
user_friendly_names <- c('TreeNumber', 'TreeAge', 'Circumference')
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
h3("Filters:"),
uiOutput("filters"),
# Plot button
fluidRow(column(2, align = "right",
actionButton("plot_graph_button", "Plot")))
),
mainPanel(tableOutput("summary"))
)
)
server <- function(input, output) {
#### Create the filter lists for UI ####
output$filters <- renderUI({
if(is.null(col_names)) return(NULL)
lapply(1:length(col_names), function(i) {
col <- paste0(col_names[i])
alias <- user_friendly_names[i]
# Populate input with unique values from column
selectizeInput(inputId = alias, label = paste(alias,':'),
choices = c('All', unique(d[[col]])), selected = 'All', multiple = T)
})
})
output$summary <- renderTable({
# Do not show a plot when the page first loads
# Wait until the user clicks "Plot" button
if (input$plot_graph_button == 0){
return()
}
# Update code below everytime the "Plot" button is clicked
input$plot_graph_button
isolate({
# Fresh copy of the full data set every time "Plot" button is clicked
d <- copy(Orange)
# Filter data based on UI
for(f in 1:length(col_names)){
print(paste("This is loop # ", f))
if(eval(parse(text = paste0('is.null(input$',user_friendly_names[f],')')))){
# If the user deleted "All" but failed to pick anything else default to "All" - do not filter
break
}else{
if(eval(parse(text = paste0('input$',user_friendly_names[f]))) != "All"){
print("FALSE -- Input is not == ALL")
d <- d[d[[col_names[f]]] == unlist(eval(parse(text = paste0('input$',user_friendly_names[f])))), ]
}else{
print("TRUE -- Input is defaulted to ALL")
}
}
}
final_summary_table <<- d
})
})
}
shinyApp(ui = ui, server = server)
My issue is that these lists are able to select multiple inputs (which I want), however, I want to initially show all available choices in all menus (which it currently does) but what I need to change is I need to have it start filtering the other lists as soon as a selection is made (no matter which list the user goes to first) based on that unique rowed data set provided.
So, if the user goes to the 2nd list and chooses tree age of 1004
then the TreeNumber
menu should change to c(1, 2, 3, 4, 5)
- no change in this scenario but the Circumference
menu should change to c(115, 156, 108, 167, 125)
, then if they pick a TreeAge
now the menus get filtered down by both TreeAge
and TreeNumber
and so on.
Right now the way the code works is it doesn't filter anything until you click "Plot", so the user might think a search will yield a bunch of results, when in reality the combination may not exist.
Here is a good example of a search that you may expect to yield a lot of results, yet it only yields 1 row:
Please note: If you do not delete 'All' it will return 'All' even if you selected other options, it is a flaw in the code that I plan to address separately along with some other minor tweaks.
I also wanted to mention that I found this post Filter one selectInput based on selection from another selectInput? which is similar to mine, however, they are dealing with menus in a top-down approach and mine is going to be more flexible about which menu the user goes to first (also mine allows multiple selections).