Search code examples
rshinyquarto

How to Get Input Based on Selected Tab in Quarto Shiny Dashboard


I’m working on a Quarto dashboard using Shiny and I’m having trouble updating a plot based on the selected tab. I have two datasets, mtcars and trees, and I want the plot to change dynamically depending on which tab is selected. Below is a snippet of my code:

---
title: "panel-tabset"
format: dashboard
server: shiny
---

```{r}
#|context: setup

data(trees)
data(mtcars)
Input {.sidebar}
::: panel-tabset

mtcars
selectInput("axis_x_mtcars",
            "axis x",
            choices = names(mtcars),
            selected = names(mtcars)[1]
)

selectInput("axis_y_mtcars",
            "axis y",
            choices = names(mtcars),
            selected = names(mtcars)[4]
)
trees
selectInput("axis_x_trees",
            "axis x",
            choices = names(trees),
            selected = names(trees)[1]
)

selectInput("axis_y_trees",
            "axis y",
            choices = names(trees),
            selected = names(trees)[2]
)
:::

Plot
plotOutput("plot_data")
#| context: server

library(ggplot2)
library(tidyverse)

dataset <- reactive({
  # if (input$active_tab == "mtcars") {
    mtcars %>%
      select(x = input$axis_x_mtcars,
             y = input$axis_y_mtcars)
  # } else {
  #   trees %>%
  #     select(x = input$axis_x_trees,
  #            y = input$axis_y_trees)
  # }
})

output$plot_data <- renderPlot({
  ggplot(dataset(), aes(x, y)) +
    geom_point()
})

I’ve commented out the conditionals for selecting the dataset based on the active tab because I’m not sure how to correctly implement the tab-switching functionality. I’m also aware that JavaScript might be needed to track the active tab and communicate it back to Shiny, but I’m not sure how to integrate it in a Quarto dashboard.


Solution

  • You can provide an id to the Tabset Panel and attach an event handler inside a Javascript chunk which in case of a tab change applies Shiny.setInputValue() for setting the active tab's name to an input value.

    enter image description here

    ---
    title: "panel-tabset"
    format: dashboard
    server: shiny
    ---
    
    ```{r}
    #| context: setup
    data(trees)
    data(mtcars)
    ```
    
    # Input {.sidebar}
    
    ::: {.panel-tabset #tabs}
    
    ## mtcars
    
    ```{r}
    selectInput("axis_x_mtcars",
                "axis x",
                choices = names(mtcars),
                selected = names(mtcars)[1]
    )
    
    selectInput("axis_y_mtcars",
                "axis y",
                choices = names(mtcars),
                selected = names(mtcars)[4]
    )
    ```
    
    ## trees
    
    ```{r}
    selectInput("axis_x_trees",
                "axis x",
                choices = names(trees),
                selected = names(trees)[1]
    )
    
    selectInput("axis_y_trees",
                "axis y",
                choices = names(trees),
                selected = names(trees)[2]
    )
    ```
    
    :::
    
    # Plot
    
    ```{r}
    plotOutput("plot_data")
    ```
    
    
    ```{r}
    #| context: server
    
    library(ggplot2)
    library(dplyr)
    
    dataset <- reactive({
      req(input$active_tab)
      if (input$active_tab == "mtcars") {
        mtcars %>%
          select(x = input$axis_x_mtcars,
                 y = input$axis_y_mtcars)
      } else {
        trees %>%
           select(x = input$axis_x_trees,
                  y = input$axis_y_trees)
      }
    })
    
    output$plot_data <- renderPlot({
      ggplot(dataset(), aes(x, y)) +
        geom_point()
    })
    ```
    
    ```{js}
    $(document).on("shiny:connected", function() {
      // initially, the first tab is chosen, set this
      var target = $('#tabs').find("a").first().text();
      Shiny.setInputValue("active_tab", target);
      
      // add an event handler which updates this value when a change occurs
      $('#tabs').on('shown.bs.tab', function (e) { 
        var target = $(e.target).text(); 
        Shiny.setInputValue("active_tab", target);
      });
    });
    ```