Populating Dynamically Created Tables

Hello,

My goal is to display query results by generating a series of tabs with a table each. The number of tabs varies with the number of rows in the dataframe returned by the database. I tried using this forum post as a reference, however it seems that the tables on all tabs display the values of the table on the final tab. Here is an example of my code:

library(shiny)
options(warn=-1)

#global variables
numTabs_g <- 1

# Define UI for data upload app ----
ui <- fluidPage(
  
  tags$head(),
  
  # App title ----
  h1(id="title", "Example Application"),
  actionButton("button","Extract information"),
  tabsetPanel(id = "tabs")
)

server <- function(input, output, session) {
  observeEvent(input$button,{
    
    #get query results
    queryResults <- data.frame("a"=c(1,4),"b"=c(2,5),"c"=c(3,6))
    
    #remove old tabs
    for(i in numTabs_g:1) {
      removeTab("tabs", toString(i))
    }
    numTabs_g <<- 0
    
    #create new tabs with tables
    for(i in 1:nrow(queryResults)) {
      appendTab("tabs",
        tabPanel(toString(i),
          fluidRow(
            column(4,shinydashboard::box(
                tableOutput(paste0("tabTable",i)),
                title = "Values",
                width = 12
              )
            )
          )
        )
      )
    }
    numTabs_g <<- nrow(queryResults)
    
    #populate tables
    observe(
      for(i in 1:nrow(queryResults)) {
        output[[paste0("tabTable",i)]] <- renderTable({
          data.frame("Markers" = c("A","B","C"),
                     "Value" = c(queryResults[i,"a"],queryResults[i,"b"],queryResults[i,"c"]))
        },rownames = FALSE, colnames = FALSE)
      }
    )
  })
}

shinyApp(ui, server)

I would expect the table on tab 1 to show {1,2,3}, but instead it shows {4,5,6}.

Hi,

Welcome to the RStudio community!

I found a workaround for your issue, here is an example:

library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      actionButton("myButton", "Click")
    ),
    mainPanel(
      tabsetPanel(id = "tabs")
    )
  )
)

server <- function(input, output, session) {
  
  #Generate 2 - 5 random data sets
  randomData = lapply(sample(10:20, sample(2:5, 1)), function(x) {data.frame(x = 1:10, y = runif(10))})
  
  observeEvent(input$myButton, {
    for(i in 1:length(randomData)){
      appendTab("tabs", 
                tabPanel(paste0("dataFrame", i),
                         tableOutput(paste0("dataTable", i))))
      
      eval(parse(text = paste0("output$", paste0("dataTable", i), 
                               "= renderTable({randomData[[", i, "]]})")))
    }
    
  })
}

shinyApp(ui, server)

For some reason the subsetting of the output did not generate the desired result for me either, with every time the same table being rendered. By using eval however, I was able to get the desired effect by parsing the whole output statement as a string and then evaluating it during the loop.

I'm hoping a Shiny expert can chip in here and explain why the logical approach is not working:

#Doesn't work
output[[paste0("dataTable", i)]] = renderTable({
        randomData[[i]]
      })

Hope this helps,
PJ

1 Like

Thank you for your solution. You helped me realize the issue was with parsing the output variable. I was able to get a solution without using eval() by using an lapply loop instead of a for loop:

library(shiny)
options(warn=-1)

#global variables
numTabs_g <- 1

# Define UI for data upload app ----
ui <- fluidPage(
  
  tags$head(),
  
  # App title ----
  h1(id="title", "Example Application"),
  actionButton("button","Extract information"),
  tabsetPanel(id = "tabs")
)

server <- function(input, output, session) {
  observeEvent(input$button,{
    
    #get query results
    queryResults <- data.frame("a"=c(1,4),"b"=c(2,5),"c"=c(3,6))
    
    #remove old tabs
    for(i in numTabs_g:1) {
      removeTab("tabs", toString(i))
    }
    numTabs_g <<- 0
    
    #create new tabs with tables
    for(i in 1:nrow(queryResults)) {
      appendTab("tabs",
                tabPanel(toString(i),
                         fluidRow(
                           column(4,shinydashboard::box(
                             tableOutput(paste0("tabTable",i)),
                             title = "Values",
                             width = 12
                           )
                           )
                         )
                )
      )
    }
    numTabs_g <<- nrow(queryResults)
    
    #populate tables
    lapply(1:nrow(queryResults), function(i) {
      output[[paste0("tabTable",i)]] <- renderTable({
        data.frame("Markers" = c("A","B","C"),
                   "Value" = c(queryResults[i,"a"],queryResults[i,"b"],queryResults[i,"c"]))
      },rownames = FALSE, colnames = FALSE)
    })
  })
}

shinyApp(ui, server)
2 Likes

Teamwork :slight_smile:

Do you know why the for loop doesn't work?

PJ

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.