0

I'm using a tabsetPanel and and I now want to create a module that adds more than one tab to this panel. Here is my example code:

library(shiny)
library(tidyverse)

resultlistUI <- function(id) {
  ns <- NS(id)
  tabPanel(
    title = "Result1",
    "Some text here 2"
  )
  tabPanel(
    title = "Result2",
    "Some text here 3"
  )
}

resultlist <- function(input, output, session) {
}

ui <- fluidPage(
  tabsetPanel(
    id = "tabs",
    tabPanel(
      title = "Tab 1",
      "Some text here"
    ),
    resultlistUI(id = "resultlist1")
  )
)

server <- function(input, output, session) {
}


shinyApp(ui = ui, server = server)

I was first wondering why I could not simply separate the two tabs with a comma. When I execute the code only my second tab "Result2" is added to the tabsetPanel. I also tried to use tagList but then none of the tabs was shown. What am I doing wrong?

EDIT: Complete minimal reproducible example added - only first tab from main app and the second tab from the module are displayed.

eastclintw00d
  • 2,250
  • 1
  • 9
  • 18
  • Shouldn't those `tabPanels()` be inside `tabsetPanel()`? – Shree Jun 08 '19 at 01:43
  • The code example I gave you is from a module. It is wrapped inside a tabsetPanel from the main script app.R. Complete minimal reproducible example added. – eastclintw00d Jun 08 '19 at 07:14

3 Answers3

2

Probably the issue come from the way the function tabsetPanel consumes its arguments. The function wants tabPanels separated per commas.

While doing what you are doing you provide two tabPanels within one argument.

So you have got to come up with something that allows you to provide one tab per argument. By returning a list from your module and using the function do.call you can achieve this.

library(shiny)
library(tidyverse)

resultlistUI <- function(id) {
  ns <- NS(id)
  list(
    tabPanel(
      title = "Result1",
      "Some text here 2"
    ),
    tabPanel(
      title = "Result2",
      "Some text here 3"
    ))
}


resultlist <- function(input, output, session) {
}


ui <- fluidPage(

  do.call(tabsetPanel, 
          list(id = 'tabs', 
               tabPanel(
                 title = "Tab 1",
                 "Some text here"
               )) %>% append(resultlistUI(id = "resultlist1")))

)

server <- function(input, output, session) {
}


shinyApp(ui = ui, server = server)
0

In addition, not sure what your module is supposed to do but you are not calling resultList inside server, you need to include both parts in your app otherwise module won't work.

Check this article. https://shiny.rstudio.com/articles/modules.html

Pablo
  • 1
0

I have found a working solution by defining multiple UI-functions within the module. The purpose is that the module takes a dataset as input and does some complicated transformations on it. I then want to display some visualizations on the first tab and to have some downloadButtons on the second tab. As both tabs refer to the same transformed data I want to encapsulate them within the same module. The example below works fine for me. I wonder if this is bad practice?

library(shiny)
library(tidyverse)

## module start
results_1_UI <- function(id) {
  ns <- NS(id)
  tabPanel(
    title = "Plot",
    plotOutput(ns("testplot"))
  )
}
results_2_UI <- function(id) {
  ns <- NS(id)
  tabPanel(
    title = "Export",
    downloadButton(ns("download_data"), "Download")
  )
}

results <- function(input, output, session, data) {
  ## do some complicated data transformations
  data_transformed <- data

  output$testplot <- renderPlot(
    {
      data_transformed %>%
        ggplot(aes(x = Sepal.Length, y = Sepal.Width)) +
        geom_point()
    })
  output$download_data <- downloadHandler(
    filename = function() {
      paste("data_", Sys.time(), ".csv", sep = "")
    },
    content = function(file) {
      write.csv(data_transformed, file, row.names = FALSE, quote = FALSE, na = "", fileEncoding = "latin1")
    },
    contentType = "text/csv"
  )
}
### module end

ui <- fluidPage(
  tabsetPanel(
    id = "tabs",
    tabPanel(
      title = "Tab 1",
      "Some text here"
    ),
    results_1_UI(id = "test1"),
    results_2_UI(id = "test1")
  )
)

server <- function(input, output, session) {
  callModule(
    module = results,
    id = "test1",
    data = iris
  )
}


shinyApp(ui = ui, server = server)
eastclintw00d
  • 2,250
  • 1
  • 9
  • 18
  • This result works, but when you put a similar element (e.g. customer selectionbox) in both UIs then they get the same name, which causes that it is not working. So be aware that the components in one UI are unique across all UIs within the module. – Jochem Aug 09 '20 at 05:10