rshinyr-leafletshiny-reactivity

filter does not respond to apply and clear buttons in my shiny app


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)

Solution

  • 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')
        )
      })
    
    }