hans glick hans glick - 1 month ago 13
R Question

R Shiny : dynamic number of tables within a tab

first I know there is a lot of threads covering my problem, I read them all, but I did not manage to do it. I got a list of 10 data.frame which I built through the following code :

list_of_df=list()
for (i in seq(1,10)){
number_row=sample(seq(5,10),size = 1)
num=seq(1,number_row)
val=sample(x = letters,size = number_row,replace = TRUE )
df=data.frame(num=num,
val=val)
rownames(df)=NULL
list_of_df[[i]]=df
}


I want the user to enter n, the number of tables he wants to see. And then display n random tables from the list_of_df. I want to display those tables inside tabs. Here is what I did, I grabbed some ideas here and there, but obviously it does not work and I do not know why.

library(shiny)

# ui function
ui = pageWithSidebar(
headerPanel('Dynamic Tabs'),
sidebarPanel(
numericInput(inputId = "numput",label = "number of tables",value = 1,min = 1,max = 5)
),
mainPanel(
uiOutput('mytabs')
)
)


# server function
server = function(input, output, session){

random_tables<- reactive({
index=sample(seq(1,10),size = input$numput,replace=FALSE)
list_of_df[[index]]
})

size<-reactive({
length(random_tables())
})

for (i in 1:size()) {
local({
my_i <- i
tablename <- paste("table_", my_i, sep="")

output[[tablename]] <- renderTable({
random_tables()[[i]]
})
})
}


output$mytabs = renderUI({
nTabs = size()
myTabs = lapply(paste0('table_', 1: nTabs), function(x){
tabPanel(x, tableOutput(x))
})
do.call(tabsetPanel, myTabs)
})
}


shinyApp(ui, server)


So, if you see what I should do ...

Answer

Here is a working version:

library(shiny)

# ui function
ui = pageWithSidebar(
  headerPanel('Dynamic Tabs'),
  sidebarPanel(
    numericInput(inputId = 'numput',label = "number of tables",value = 1,min = 1,max = 5)
  ),
  mainPanel(
    uiOutput('mytabs')  
  )
)

# server function
server = function(input, output, session){
  list_of_df=list()
  for (i in seq(1,10)){
    number_row=sample(seq(5,10),size = 1)
    num=seq(1,number_row)
    val=sample(x = letters,size = number_row,replace = TRUE )
    df=data.frame(num=num,
                  val=val)
    rownames(df)=NULL
    list_of_df[[i]]=df
  }
  random_tables<- reactive({
    index=sample(seq(1,10),size = input$numput,replace=FALSE)
    list_of_df[index]
  })

  size<-reactive({
    input$numput
  })

  observe({
    lapply(seq_len(size()), function(i) {
      local({
        my_i <- i
        tablename <- paste("table_", my_i, sep="")
        output[[tablename]] <- renderTable({
          random_tables()[[i]]
        })
      })
    })
  })

  output$mytabs = renderUI({
    nTabs = size()
    myTabs = lapply(paste0('table_', seq_len(nTabs)), function(x){
      tabPanel(x, tableOutput(x))
    })
    do.call(tabsetPanel, myTabs)
  })

}

shinyApp(ui=ui,server=server)

A couple issues, you subset the list with double brackets, but it isn't working like you think it is, you need single brackets. Next when you select a single table random_table() is a data.frame so when you call length you get 2, the number of columns. So just use the input$numput for size() since they are the same anyways. Also, I put the dynamic output in an observe so that it can access the reactive size(). A small thing, but I used seq_len instead of 1:aNumber since it is more robust.

Hope this helps

Comments