Search code examples
rshinyr-markdownflexdashboard

How to get the current active tab in a Flexdashboard document to reactively display different UI


I am currently trying to make a Flexdashboard shiny app. I want to be able to display different UI in my sidebar given which tab is active in my output zone.

For this I considered using a renderUI and some basic conditions. But my problem is that I don't know how to get the active tab in flexdashboard. I have done some research but I wasn't able to find the info. I am not sure if we can add id to a tabset as in shiny and if it is possible, how to get the result corresponding to the id.

Here is some skeleton code of what I would like to do :

---
title: "Test"
runtime: shiny
output:
  flexdashboard::flex_dashboard:
    vertical_layout: scroll
---
```{r setup, include=FALSE}
suppressPackageStartupMessages({
library(shiny)
library(flexdashboard)
})
```


# Page 1

## Input {.sidebar}

```{r}
radioButtons("radio", label = h3("Radio buttons"),
  choices = list("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3), 
  selected = 1)


output$list1 <- renderUI({
  if (tab == "Tabs 1") {
    selectInput("select", label = h3("Select box"), 
      choices = list("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3), 
      selected = 1)
  } 
  else {}
})

uiOutput("list1")
```

Column {.tabset }
-------------------------------------

### Tab 1

### Tab 2

### Tab 3

Thanks for your help :)


Solution

  • It seems that unlike in the tabsetPanel(..., id = "xyz") case where there is a input$xyz variable created, Rmarkdown does not show the same courtesy.

    Hence, we need to hack that functionality ourselves with the help of a bit JavaScript:

    ---
    title: "Test"
    runtime: shiny
    output:
      flexdashboard::flex_dashboard:
        vertical_layout: scroll
    ---
    <script>
    $("body").on("shown.bs.tab", "a[data-toggle='tab']", function(e) {
       Shiny.setInputValue("active_tab", $(e.target).parent().index());
    })
    </script>
    

    We are telling shiny that whenever a tab is shown, we want to set an input variable (i.e. something we can react upon on the shiny side) to the corresponding tab number (0 based).

    We can then use input$active_tab to set our server logic:

    output$list1 <- renderUI({
       sel <- input$active_tab
       if (is.null(sel) || sel == 0) {
          selectInput("select", label = h3("Select box"),
                      choices = list("Choice A1", "Choice A2", "Choice A3"),
                      selected = 1)
       } else if (sel == 1) {
          selectInput("select", label = h3("Select box"),
                      choices = list("Choice B1", "Choice B2", "Choice B3"),
                      selected = 1)
       } else {
                selectInput("select", label = h3("Select box"),
                      choices = list("Choice C1", "Choice C2", "Choice C3"),
                      selected = 1)
       }
    })
    

    Note. I guess that there is a better event rather than shown.bs.tab to which you can attach the event handler. This may help to avoid testing for the NULL case in teh render part. This happens, because apparently shown.bs.tab does not fire when the first tab is shown by default.