Search code examples
rshinytabsaction-button

Is it possible to have the same actionButton in 2 tabs [Shiny]?


I am trying to create an app which has 3 tabs:

  • Tab 1 > it has a checkboxInput that if you click on it, you will do the log2 transformation. In addition, it has one actionButton to submit your data (with or without log2)
  • Tab 2 > it has a sliderInput which allows you to change the bins of the histogram. It has another actionButton, but this time, to show the plot.
  • Tab 3 > it has a numericInput that allows you to change the opacity of the plot. In addition, it has the same actionButton that you find in Tab 2.

It works perfectly. However, when I change the opacity in Tab 3 and I want to update the plot, the actionButton from the Tab 3 doesn't work. However, if you click the actionButton from Tab 2 after having changed the opacity, the plot updates.

image 1

So... for that reason, I was wondering if there was a way to have the same actionButton in two tabs.

The code:

library(shiny)
library(dplyr)
library(ggplot2)

hist_function <- function(DF, bins, alpha){
  hist <- DF %>% 
    ggplot(aes(value)) +
    geom_histogram(aes(y=..density.., fill = id), bins=bins, col="black", alpha=alpha) +
    geom_density() +
    facet_grid(id ~ .) + 
    ggtitle("My histogram....") +
    theme(strip.text.x = element_blank(),strip.text.y = element_blank()) 
  
  return(hist)
}
  
ui <- fluidPage(
  
  titlePanel("My app"),
  
  sidebarLayout(
    sidebarPanel(
      tabsetPanel(
        
        tabPanel("Tab1",
                 checkboxInput("log2", "Log2 transformation", value = FALSE),
                 actionButton("submit", "Submit")
        ),
        
        tabPanel("Tab2",
                 sliderInput("bins",
                             "Number of bins:",
                             min = 1,
                             max = 50,
                             value = 10),
                 actionButton("show_plot", "See the plot")
        ),
        
        tabPanel("Tab3",
                 numericInput("alpha", "Opacity of the histogram", value=0.2),
                 actionButton("show_plot", "See the plot")
        )
        
      )
    ),
    
    mainPanel(
      plotOutput("histogram"),

    )
  )
)


server <- function(input, output) {
  
  val1 <- c(2.1490626,3.7928443,2.2035281,1.5927854,3.1399245,2.3967338,3.7915825,4.6691277,3.0727319,2.9230937,2.6239759,3.7664386,4.0160378,1.2500835,4.7648343,0.0000000,5.6740227,2.7510256,3.0709322,2.7998003,4.0809085,2.5178086,5.9713330,2.7779843,3.6724801,4.2648527,3.6841084,2.5597235,3.8477471,2.6587736,2.2742209,4.5862788,6.1989269,4.1167091,3.1769325,4.2404515,5.3627032,4.1576810,4.3387921,1.4024381,0.0000000,4.3999099,3.4381837,4.8269218,2.6308474,5.3481382,4.9549753,4.5389650,1.3002293,2.8648220,2.4015338,2.0962332,2.6774765,3.0581759,2.5786137,5.0539080,3.8545796,4.3429043,4.2233248,2.0434363,4.5980727)
  val2 <- c(3.7691229,3.6478055,0.5435826,1.9665861,3.0802654,1.2248374,1.7311236,2.2492826,2.2365337,1.5726119,2.0147144,2.3550348,1.9527204,3.3689502,1.7847986,3.5901329,1.6833872,3.4240479,1.8372175,0.0000000,2.5701453,3.6551315,4.0327091,3.8781182)
  
  data <- reactive({
    df1 <- data.frame(value = val1)   
    df2 <- data.frame(value = val2)   
    
    data_df <- bind_rows(lst(df1, df2), .id = 'id')
  })
 
  
  mydata <- reactive({
    req(input$submit)
    
    if(input$log2 == TRUE){
      data <- data()
      cols <- sapply(data, is.numeric)
      data[cols] <- lapply(data[cols], function(x) log2(x+1))
    
    }
    else{
      data <- data()
    }
    return(data)
  })
  
  
  v <- reactiveValues()
  observeEvent(input$show_plot, {
     v$plot <- hist_function(DF=mydata(), bins=input$bins, alpha=input$alpha)
  })
  
  output$histogram <- renderPlot({
    req(input$submit)
    if (is.null(v$plot)) return()
    v$plot
  })
  
}

shinyApp(ui = ui, server = server)

Note that if I put the actionButton when the tabsetPanel finishes, the actionButton works in Tab 3, but it appears in Tab 1, which I don't want. I only want to have the submit button in that tab.

ui <- fluidPage(
  
  titlePanel("My app"),
  
  sidebarLayout(
    sidebarPanel(
      tabsetPanel(
        
        tabPanel("Tab1",
                 checkboxInput("log2", "Log2 transformation", value = FALSE),
                 actionButton("submit", "Submit")
        ),
        
        tabPanel("Tab2",
                 sliderInput("bins",
                             "Number of bins:",
                             min = 1,
                             max = 50,
                             value = 10),
                 #actionButton("show_plot", "See the plot")
        ),
        
        tabPanel("Tab3",
                 numericInput("alpha", "Opacity of the histogram", value=0.2),
                 #actionButton("show_plot", "See the plot")
        )
        
      ),
      actionButton("show_plot", "See the plot") ### THIS IS THE NEW BUTTON
      
    ),
    
    mainPanel(
      plotOutput("histogram"),

    )
  )
)

image 2

On the other hand, I was thinking to create two different buttons... and therefore, I would have to add the new one to the observeEvent... but, I wanted to ask here just in case someone knows how to do it without creating a new button.

Thanks very much in advance

Regards


Solution

  • You can't have two or more inputs with the same IDs, but it doesn't mean you need to have two observEvents in your case.

    Change the ID of the second button:

    tabPanel("Tab3",
                     numericInput("alpha", "Opacity of the histogram", value=0.2),
                     actionButton("show_plot_2", "See the plot")
    

    And change the oberveEvent as below:

    observeEvent(input$show_plot | input$show_plot_2, {
        v$plot <- hist_function(DF=mydata(), bins=input$bins, alpha=input$alpha)
      })
    

    However, please note that now user can change number of bins, do not click the "see the plot" button, go to tab3, change the opacity, click "see the plot" and plot will be updated taking into account new number of bins and new opacity - take into account if this will be surprising for user or not. If you don't want this, then indeed you will need two observeEvents.

    I see that plot is displayed only if input$submit is clicked at least once in session, it looks strange for me, so I'm leaving this note :).