Shiny: Reuse the same output binding on different tab panels

Created on 17 Jun 2015  路  10Comments  路  Source: rstudio/shiny

Per a suggestion from Dean Attali @daattali, I am opening up this issue pertaining to this SO post.

Basically, I would like to be able to re-use the same output binding. One possible use case is with a tabPanel-sidebarPanel layout -- consider an output graph/visualization (in my post, a visNetwork) where the user is using widgets to manipulate the output on one tabPanel and then when they switch to a different tabPanel that same output is still in the mainPanel area, but now it can be manipulated by a different set of widgets.

I was able to solve this using a conditionalPanel and renderUI if that helps.

Here's some snippets:

Currently, when I try to run the code similar to below, I receive an error: Uncaught Duplicate binding for ID vis.


Snippet of server.R

myNet <- reactive({
  nodes <- df_nodes
  edges <- df_edges 
  visNetwork(nodes, edges, height = '800px')
})

output$vis <- renderVisNetwork(
  myNet()
)

Snippet of ui.R

...

tabPanel("First Panel",
  sidebarLayout(
    sidebarPanel(
      sliderInput("input1", "Title 1", 
        min=1, max=10, value=1),
      sliderInput("input2", "Title 2",
        min=1, max=10, value=1),
      sliderInput("input3", "Title 3",
        min=1, max=10, value=1)
    ),
    mainPanel(
      visNetworkOutput("vis", height = '800px') # *** ISSUE HERE***
    )
  )
),
tabPanel("Second Panel",
  sidebarLayout(
    sidebarPanel(
      sliderInput("input4", "Title 4", 
        min=1, max=10, value=1),
      sliderInput("input5", "Title 5",
        min=1, max=10, value=1),
    ),
    mainPanel(
      visNetworkOutput("vis", height = '800px') # *** ISSUE HERE*** 
    )
  )
), ...

A not so DRY solution is:

output$vis_1 <- renderVisNetwork(myNet())
output_vis_2 <- renderVisNetwork(myNet())

etc.

Most helpful comment

Allowing multiple outputs to share the same ID would have a lot of gnarly side effects, it makes my head hurt to even think about what it would mean. For example, two Leaflet maps with the same ID--but they both have lots of state in the browser, and communicate that state back to the client. How can you make sense of that when they share the same ID?

How about this instead?

output$vis_1 <- output$vis_2 <- renderVisNetwork(myNet())

I've never suggested this before because I didn't realize it would work... but it seems to. To me it seems like a pretty elegant solution.

All 10 comments

The reason I suggested bringing this up to discussion is because I've noticed multiple people asking this exact question recently. From what I recall, the solution offered by @jcheng5 was to try to use the header parameter of the tabpanel since that part is used by all tabs. The other solution I've seen being suggested is the "not-so-DRY" approach mentioned at the end. I was just wondering if maybe there should be an official native shiny solution, or is one of the two mentioned solutions the official recommendation.

Allowing multiple outputs to share the same ID would have a lot of gnarly side effects, it makes my head hurt to even think about what it would mean. For example, two Leaflet maps with the same ID--but they both have lots of state in the browser, and communicate that state back to the client. How can you make sense of that when they share the same ID?

How about this instead?

output$vis_1 <- output$vis_2 <- renderVisNetwork(myNet())

I've never suggested this before because I didn't realize it would work... but it seems to. To me it seems like a pretty elegant solution.

@jcheng5 genius! :+1:

@jcheng5 I'm using a couple of inputs on a shiny app wherein I have multiple tabPanels containing plots that are affected by thesevery same inputs.

How can I align them right below the top black navbar throughout all pages, ensuring that they:

  1. Remain constant throughout all tabs
  2. Don't refresh when tabPanels are changed as the rendering data will start again.

*_As seen in the image below, the 5 inputs are coming at the bottom of each page. *_
image

Page format is as follows:

shinyUI(
  navbarPage(theme=shinytheme("cosmo"),
             tabPanel("Summary",htmlOutput("summary_anims")),

             tabPanel("Order Online",column(7,plotlyOutput("plot_oo"))),

             tabPanel("Conversions", fluidRow(column(4,h3("YOU CAN DO IT!")),
                                              column(8, plotlyOutput("plotly_conv"))
                      )),

                      windowTitle="aTitle",
                      collapsible=TRUE,
#Inputs are common for all tabPanels but appear on the bottom of each tabPanel irrespective of the length.
                      fixedRow(
                        column(2, selectInput("city","City:",c(...))),
                        column(2, selectInput("name","Sub:",c(...)),
                        column(2, selectInput("client","Client:",c(...))),
                        column(2, selectInput("poc", "POC", c(...))))),
                        column(2, selectInput("range","Range:",c(...)))
                      ))

@jcheng5 I think modules are the answer to the original issue, so this could perhaps be closed?

The question by @kapilbahadur is related but slightly different, it asks about ensuring consistent position within different tabs.

@rstudio-shiny (I thought maybe this tag would tag all the people on the shiny team)

@kapilbahadur how did you do the three panels with values in your post? I refer to the boxes with 3.42, 5.00 and 1.32? I love the design and wonder how to create it.

@daattali, @jcheng5 Hi I have been playing around with modules after watching the excellent webinar last night, now I have created a module that brings up a pop-up (modal) where the user can edit some options. This module then returns all the options that have been set and those options then influence graphs/tables on all the tabs.
Now I want the buttons on different tabs to call the same module and edit global options, but their ids need to be unique. How do I get both modules to edit the same options? See simple example below:

library(shiny)


modal.options.UI<-function(id){
  ns<-NS(id)
  tagList(
    actionButton(ns("showModal"),"Show")
  )

}

modal.options<-function(input,output,session,options){
  modal<-function(ns,options){
    modalDialog(
      size = "s",
      title = "Upload Data",
      easyClose = TRUE,
      fade = TRUE,

      checkboxInput(ns("check1"),"Option 1",value = options$check1),

      footer=tagList(
        actionButton(ns("submit"), "Submit",width = "100%")
      )
    )
  }

  observeEvent(input$showModal,{
    ns <- session$ns
    showModal(modal(ns=ns,options))
  })

  observeEvent(input$submit,{
    options$check1<-input$check1
    removeModal()
  })

  return(options)
}



ui <- fluidPage(
  tabsetPanel(
    tabPanel(
      "Tab1",
      modal.options.UI("options"),
      textOutput("out1")
    ),
    tabPanel(
      "Tab2",
      modal.options.UI("options"),
      textOutput("out2")
    )
  )
)

server <- function(input, output) {

   options<-callModule(modal.options,"options",options=if(exists("options")){options}else{reactiveValues(check1=FALSE)})
   output$out1<-output$out2<-renderText({options$check1})

}

shinyApp(ui = ui, server = server)

I solved it, by having two and passing the reactive values, and editing them within the modal worked. i.e.

options<-callModule(modal.options,"options1",options=if(exists("options")){options}else{reactiveValues(check1=FALSE)})
options<-callModule(modal.options,"options2",options=if(exists("options")){options}else{reactiveValues(check1=FALSE)})

I think this should have been closed when @daattali posted in Sep 2016.

@jcheng5 I think modules are the answer to the original issue, so this could perhaps be closed?

Correct.

(Hey @daattali, we're only taking 9 months!)

Better late than never! I still stand by what I said back then - it'd be cool if you made a github "team" so that we could tag @shiny-team and it would automatically alert whoever's relevant

I think this (@rstudio/shiny) should do it (I just created a team with that name, but this may prove annoying since not all of us are working on Shiny at all times... we'll see)

Was this page helpful?
0 / 5 - 0 ratings