Shinydashboard: render only the clicked tab

Hello.
I am working on shinydashboard app with multiple tabs & I would like to render the tab content only when it is clicked on.
Atm the app is organized from most general to most detailed tab with general tab displaying when the app starts. This follows from the logical principle, that it is 1.the most used tab 2.the lightest one in terms of plot rendering (plots in all tabs are more or less the same but more detailed plots mean more information needs to be processed). Each tab has a different dataframe/sql table behind it (with the general tab, it is more efficient to just collect the whole sql table once, this might change with the heavier tabs).

What is the optimal way to trigger data collection when a different tab is clicked?
How should be the data stored(once rendered), so that when you get back to a tab that was previously rendered, you don't need to do it again? I am specifically looking for an generalized implemantation using shiny modules & functions.

Thank you.

Hi,

By design, Shiny will only run all code once unless it has reactive content that is updated. This does mean that the code for all tabs is generated at launch, but then not anymore.

You can force Shiny to wait with the execution of tab code till you click the tab, if indeed it's very computationally expensive code and the user is not likely to click it anyway.

Find below a dummy example how this could be implemented (you can of course make it much more complex than that)

library(shiny)

ui <- fluidPage(
  tabsetPanel(id = "tabs",
    tabPanel(value = "tab1", title = "Tab 1",
             tableOutput("myTable")
             ),
    tabPanel(value = "tab2", title = "Tab 2",
             plotOutput("myPlot")
             )
  )
)

server <- function(input, output, session) {
  
  tableData = reactiveVal()
  plotData = reactiveVal()
  
  observeEvent(input$tabs, {

    if(input$tabs == "tab1"){
      #Code for tab 1
      req(is.null(tableData()))
      print("Tab 1 code is run")
      tableData(data.frame(x = 1:10, y = LETTERS[1:10]))
      
    } else if(input$tabs == "tab2"){
      #Code for tab 2
      req(is.null(plotData()))
      print("Tab 2 code is run")
      plotData(runif(100))
      
    }
    
  })
  
  output$myTable = renderTable({
    tableData()
  })
  
  output$myPlot = renderPlot({
    plot(plotData())
  })
}

shinyApp(ui, server)

Since the code in tab 1 will always have to be rendered at start anyway, you can also write the code like this:

library(shiny)

ui <- fluidPage(
  tabsetPanel(id = "tabs",
    tabPanel(value = "tab1", title = "Tab 1",
             tableOutput("myTable")
             ),
    tabPanel(value = "tab2", title = "Tab 2",
             plotOutput("myPlot")
             )
  )
)

server <- function(input, output, session) {
  
  tableData = reactiveVal(data.frame(x = 1:10, y = LETTERS[1:10]))
  plotData = reactiveVal()
  
  observeEvent(input$tabs, {

    if(input$tabs == "tab2"){
      #Code for tab 2
      req(is.null(plotData()))
      print("Tab 2 code is run")
      plotData(runif(100))
      
    }
    
  })
  
  output$myTable = renderTable({
    tableData()
  })
  
  output$myPlot = renderPlot({
    plot(plotData())
  })
}

shinyApp(ui, server)

Hope this helps,
PJ

1 Like

Thanks PJ!
Though I am struggling a bit to implement that in the context of shinydashboard:

ui = dashboardPagePlus(
  dashboardHeaderPlus(title =  "Dashboard",
                      enable_rightsidebar = TRUE),
  dashboardSidebar(
    sidebarMenu(
      id = "sidebarmenu",
      menuItem("Tab1", "tab1", "dashboard"),
      menuItem("Tab2", "tab2", "dashboard")
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(tabName = "tab1"),
      tabItem(tabName = "tab2")
    )
  )
)

Do you know which element should be in observeEvent in this case? I iterated over few options without luck.

Hi,

How about this:

library(shiny)
library(shinydashboardPlus)

ui = dashboardPagePlus(
  dashboardHeaderPlus(title =  "Dashboard",
                      enable_rightsidebar = TRUE),
  dashboardSidebar(
    sidebarMenu(
      id = "sidebarmenu",
      menuItem("menuItem1", tabName = "tab1"),
      menuItem("menuItem2", tabName = "tab2")
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(tabName = "tab1",
              tableOutput("myTable")),
      tabItem(tabName = "tab2",
              plotOutput("myPlot"))
    )
  )
)

server <- function(input, output, session) {
  
  tableData = reactiveVal(data.frame(x = 1:10, y = LETTERS[1:10]))
  plotData = reactiveVal()

  observeEvent(input$sidebarmenu, {

    if(input$sidebarmenu == "tab2"){
      #Code for tab 2
      req(is.null(plotData()))
      print("Tab 2 code is run")
      plotData(runif(100))

    }

  })

  output$myTable = renderTable({
    tableData()
  })

  output$myPlot = renderPlot({
    plot(plotData())
  })
}

shinyApp(ui, server)

Grtz,
PJ

2 Likes

Thanks a lot! I must have made some mistake when trying to do it myself.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.