I'm building a shiny app to visualize hotels on map (using leaflet package). I have a dropdown list which I can load a subset of data. In addition to that, I also want to build a filter button at the upper left corner, in which I can use to further filter data by year.
I would like the filter only respond when I click on apply
button. Also, I would like to clear the filter once I click on clear
button.
Below is my shiny app code. It seems like my apply
and clear
button does not work. My map refresh once I select a year from the filter dropdown. I want it to refresh only after I click on apply
button. The filter also does not respond to 'clear'. I spent hours but unable to figure out why. Does anyone know what I did wrong.
This is a much simplified version. My actual app is lot more complicated. Have multiple filters in the dropdown button.
library(shiny)
library(leaflet)
library(shinyjs)
library(shinyWidgets)
library(dplyr)
df <- read.table(text = "Name Year Value longitude latitude
A 2020 12 -106.50645 35.06745
B 2020 33 -116.00994 36.21834
C 2020 44 -115.24685 36.29460
A 2019 55 -115.24685 35.06745
B 2019 22 -118.18642 33.98881
C 2019 11 -111.83064 35.06745",
header = TRUE)
ui <- fluidPage(
selectizeInput(inputId = 'hotel', label='Hotel Type:',
choices = c('A', 'B', 'C'),
multiple=TRUE,
options = list(
maxItems = 1,
placeholder = '',
onInitialize = I("function() { this.setValue(''); }"))),
actionButton("load", "load"),
div(id = 'map_filter',
dropdown(
tags$h3("Filters"),
selectizeInput(inputId = 'year', label='Choose Year:',
choices = c(2020,2019),
multiple=TRUE,
options = list(
maxItems = 2,
placeholder = '',
onInitialize = I("function() { this.setValue(''); }"))),
#uiOutput('map_zipcode_prod2ui') ,
# column(width=1, offset = 1, actionButton(inputId='map_zipcode_applyProductFilter',
# label='Apply Filter', style = 'margin-top:25px') )
actionBttn(
inputId = 'applyFilter',
label = "Apply",
style = "gradient",
color = "danger",
icon = icon("") ) ,
actionBttn(
inputId = 'clearFilter',
label = "Clear",
style = "gradient",
color = "danger",
icon = icon("") ) ,
style = "unite", icon = icon("filter"),
status = "danger", width = "300px"
) #dropdown
), #div
leafletOutput("mymap")
)
server <- function(input, output, session) {
df1 = eventReactive (input$load, {
df %>% filter(Name == input$hotel)
})
df2 = reactive({
if (length(input$year)==0) {
df2 = df1()
} else {
df2 = df1() %>% filter(Year %in% input$year)
}
})
# clear all filters
observeEvent(input$clearFilter, {
reset("map_filter")
})
observeEvent(c(input$load,input$applyFilter), ignoreInit = T, ignoreNULL = T, {
output$mymap <- renderLeaflet({
map <- leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
addCircleMarkers( data = df2(),
#group = 'Data Markers 1',
lng = ~longitude,
lat = ~latitude,
radius = 10,
stroke = F,
fillOpacity = 0.9,
color = 'red')
})
})
}
shinyApp(ui, server)
Here is one approach - hope this is helpful.
You can store the map in a reactiveVal
. Then, you update the map with your load or apply filter buttons. Otherwise, the map won't change.
Would consider using updateSelectizeInput
to clear your filter.
Edit: Based on comment, df1
and df2
are not combined.
server <- function(input, output, session) {
map <- reactiveVal(NULL)
df1 = reactive({
req(input$hotel)
filter(df, Name == input$hotel)
})
df2 = reactive({
if (length(input$year)==0) {
df2 = df1()
} else {
df2 = df1() %>% filter(Year %in% input$year)
}
})
# clear all filters
observeEvent(input$clearFilter, {
updateSelectizeInput(session, inputId = 'year', selected = "")
})
output$mymap <- renderLeaflet({
map()
})
observeEvent(c(input$load, input$applyFilter), ignoreInit = T, ignoreNULL = T, {
map(leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
addCircleMarkers( data = df2(),
#group = 'Data Markers 1',
lng = ~longitude,
lat = ~latitude,
radius = 10,
stroke = F,
fillOpacity = 0.9,
color = 'red')
)
})
}