4

I am making an application using shiny that uses multiple tabs. On one tab (not the starting tab) I display a leaflet map, which is controlled by widgets on different tabs.

The issue is that if I change the inputs on one tab, without visiting the map first, and then visit the map, the map is not updated. However, changing the inputs after or while being on the map tab, the map is updated.

To replicate the issue:

  1. run the following code
  2. change the input color to blue (without visiting the map tab first!)
  3. go to the "Map" tab
    • expected behavior: blue dot. Actual behavior: no dot (not yet drawn, as if the observe() didn't get triggered (note that observe is triggered but has no effect), or the leaflet was not rendered)
  4. change the color to yellow
    • yellow dot appears
  5. go to the "Other" tab and change color to green
  6. go back to the "Map" tab and see a green dot

Similarly, when the app is started and we directly go to the map, I would expect a red dot to be displayed. Instead there is no dot.

Code:

library(shiny)
library(leaflet)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("color", "Color", choices = c("red", "blue", "yellow", "green"))
    ),
    mainPanel(
      tabsetPanel(
        tabPanel("Other", h1("Empty Tab 1")),
        tabPanel("Map", leafletOutput("map"))
      )
    )
  )
)

server <- function(input, output, session) {
  # the base map and the circles are separated due to data restrictions
  # in the actual app!
  output$map <- renderLeaflet({
    leaflet() %>% 
      addTiles()
  })
  observe({
    leafletProxy("map") %>% 
      addCircles(lng = 0, lat = 0, radius = 3e6, color = input$color)
  })
}

shinyApp(ui, server)

I suspect that leafletProxy doesn't work as the leaflet widget is not yet rendered in step 2.

Any idea how to fix this issue?

In my real-life app, the first tab allows the user to upload data, while the second tab displays the data using leaflet, therefore the leaflet map gets updated and configured before its actually rendered...

Note that creating the leaflet in one go (Creating the map in the observe() without leafletProxy) is not an option due to various reason (size of leaflet, being dependent on other reactive values, scenarios etc.).

David
  • 9,216
  • 4
  • 45
  • 78

1 Answers1

5

This works with the output option suspendWhenHidden set to FALSE and by adding a dependency to the tabset in the observer:

library(shiny)
library(leaflet)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("color", "Color", choices = c("red", "blue", "yellow", "green"))
    ),
    mainPanel(
      tabsetPanel(
        tabPanel("Other", h1("Empty Tab 1")),
        tabPanel("Map", leafletOutput("map")),
        id = "tabset"
      )
    )
  )
)

server <- function(input, output, session) {
  
  output$map <- renderLeaflet({
    leaflet() %>% 
      addTiles()
  })
  outputOptions(output, "map", suspendWhenHidden = FALSE)
  
  proxy <- leafletProxy("map")
  
  observeEvent(list(input$tabset, input$color), {
    proxy %>% 
      addCircles(lng = 0, lat = 0, radius = 3e6, color = input$color)
  })
}

shinyApp(ui, server)
Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • that is wonderful, thank you very much! Didnt know about `suspendWhenHidden` and the ability depend on the `tabset`.... – David Jul 02 '20 at 17:11
  • I tried this method and now my application cannot process updateSelectizeInput. Rather, the function is ran, but the client never updates after running. If I remove outputOptions() the app behaves normally and the selectizeInput boxes are updated on the client. – kraggle Sep 28 '22 at 18:34