ruser-interfaceshiny

How do I reuse a user element in R shiny?


I am writing a shiny app where I have a sidebarPanel containing various interactive user elements. This sidebarPanel is embedded in the ui in such a way:

Server/UI

library(shiny)

ui <- navbarPage(
  title="some title",
  tabPanel(
    title="Tab 1",
    sidebarLayout(
      # This is the element I want to reuse, see second tabPanel
      sidebarPanel(radioButtons(inputId="button1", label="Choose", choices=c("1", "2"))),
      mainPanel(uiOutput("output1"))
    )
  ),
  tabPanel(
    title="Tab 2",
    sidebarLayout(
      sidebarPanel(radioButtons(inputId="button1", label="Choose", choices=c("1", "2"))),
      mainPanel(uiOutput("output2"))
    )
  )
)
  
    
server <- function(input, output, session) {
  output$output1 <- renderText("Text 1")
  output$output2 <- renderText("Text 2")
}

shinyApp(ui=ui, server=server)

I have tried creating a module such as they did in Translate shiny app using shiny.i18n in modules?

Module module.R

library(shiny)

samplePanel <- function(id) {
  ns <- NS(id)
  sidebarPanel(
    radioButtons(inputId="button1", label="Choose", choices=c("1", "2")),
    textOutput("sample_text")
  )
}

sampleModule <- function(input, output, session) {
  output$sample_text <- "Sample text"
}

New Server/UI main script

source("module.R")

library(shiny)

ui <- navbarPage(
  title="some title",
  tabPanel(
    title="Tab 1",
    sidebarLayout(
      samplePanel("moduleId"),
      mainPanel(uiOutput("output1"))
    )
  ),
  tabPanel(
    title="Tab 2",
    sidebarLayout(
      samplePanel("moduleId"),
      mainPanel(uiOutput("output2"))
    )
  )
)


server <- function(input, output, session) {
  callModule(sampleModule, "moduleId")
  output$output1 <- renderText("Text 1")
  output$output2 <- renderText("Text 2")
}

shinyApp(ui=ui, server=server)

What is it I don't understand about modularizing shiny code? One thing that surprises me is that my samplePanel doesn't even show the sample_text I want it to show.


Solution

  • Does this give you something close to what you want?

    library(shiny)
    
    samplePanel <- function(id) {
      ns <- NS(id)
      sidebarPanel(
        radioButtons(inputId=ns("button1"), label="Choose", choices=c("1", "2")),
        textOutput(ns("sample_text"))
      )
    }
    
    sampleModule <- function(input, output, session) {
      output$sample_text <- renderText("Sample text")
    }
    
    ui <- navbarPage(
      title="some title",
      tabPanel(
        title="Tab 1",
        sidebarLayout(
          samplePanel("moduleId1"),
          mainPanel(uiOutput("output1"))
        )
      ),
      tabPanel(
        title="Tab 2",
        sidebarLayout(
          samplePanel("moduleId2"),
          mainPanel(uiOutput("output2"))
        )
      )
    )
    
    server <- function(input, output, session) {
      callModule(sampleModule, "moduleId1")
      callModule(sampleModule, "moduleId2")
      output$output1 <- renderText("Text 1")
      output$output2 <- renderText("Text 2")
    }
    
    shinyApp(ui=ui, server=server)
    

    In response to discussion in comments, this layout moves the module from the tabPanel sidebar to the page sidebar, meaning that a single instance is common to each of the tabPanels. As there's now only a single instance, there may not be any need to use a module.

    library(shiny)
    
    samplePanel <- function(id) {
      ns <- NS(id)
      sidebarPanel(
        radioButtons(inputId=ns("button1"), label="Choose", choices=c("1", "2")),
        textOutput(ns("sample_text"))
      )
    }
    
    sampleModule <- function(input, output, session) {
      output$sample_text <- renderText("Sample text")
    }
    
    ui <- navbarPage(
      title="some title",
        samplePanel("moduleId1"),
        mainPanel(
          tabsetPanel(
          tabPanel(
            title="Tab 1",
            uiOutput("output1")
          ),
          tabPanel(
            title="Tab 2",
            uiOutput("output2")
          )
        )
      )
    )
    
    server <- function(input, output, session) {
      callModule(sampleModule, "moduleId1")
      output$output1 <- renderText("Text 1")
      output$output2 <- renderText("Text 2")
    }
    
    shinyApp(ui=ui, server=server)