Reputation: 8506
I am creating an app with modules, based on this answer. Basically, it is an app in which it is possible to create identical tabs with different input names, just by clicking on a tab called More
.
Now, I would like to give the user the possibility to merge some (or all) of the tables created. To do so, there is a (permanent) tab called Merge
in which there is a checkBoxInput
. When no tab is created, this checkBoxInput
is empty (since there are no tab and hence no table to select). When one tab and therefore one table are created, I would like the checkBoxInput
to be updated to display a box and the name corresponding to this table. For example, if I create 3 tabs, then there should be 3 boxes in the checkBoxInput
.
My idea so far was to store the tables created in a list and to update checkBoxInput
with the content of this list each time a tab and a table are created. However, I don't know how to obtain the name of the tables created in a module. Since the tables are named with x
in the module moduleTable
, I thought I could just use x
but it gives me the following error:
Warning: Error in observeEventHandler: object 'x' not found
Below is a reproducible example:
library(shiny)
library(shinyWidgets)
library(dplyr)
addTab <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("select"),
"Choose",
choices = colnames(mtcars)),
tableOutput(ns("table"))
)
}
moduleTable <- function(input, output, session){
x <- reactive(select(mtcars, input$select))
output$table <- renderTable({
x()
})
}
ui <- navbarPage(position = "static-top",
title = "foo",
id = "tabs",
tabPanel(title = "Merge",
fluidRow(
checkboxGroupInput("to_merge",
label = "Tables to merge",
choices = NULL)
)),
tabPanel(title = "More",
icon = icon("plus"),
fluidRow()
)
)
server <- function(input, output, session) {
count <- reactiveValues(val=0)
dfs <- list()
observeEvent(input$tabs, {
if (input$tabs == "More"){
count$val <- count$val+1
name <- paste0("Name ", count$val)
insertTab(inputId = "tabs",
tabPanel(title = name,
addTab(paste0("select", count$val))
),
target = "More",
position = "before",
select = TRUE)
callModule(moduleTable, paste0("select", count$val))
dfs[[count$val]] <- paste0("mtcars$select", count$val)
# UNCOMMENT THE LINE BELOW AND COMMENT THE LINE ABOVE TO SEE THE PROBLEM
# tables[[count$val]] <- x
names(dfs[count$val]) <- paste0("df", count$val)
updateCheckboxGroupInput(session = session,
inputId = "to_merge",
choices = names(dfs))
}
})
}
shinyApp(ui = ui, server = server)
How can I obtain the names of the dataframes created and store them in a reactive list?
Upvotes: 0
Views: 57
Reputation: 51
You may try the following code to return the reactive expression and join the table.
library(shiny)
library(shinyWidgets)
library(tidyverse)
addTab <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("select"),
"Choose",
choices = colnames(mtcars[, -1])),
tableOutput(ns("table"))
)
}
moduleTable <- function(input, output, session){
x <- reactive(select(mtcars, c(mpg, input$select)))
output$table <- renderTable({
x()
})
return(x)
}
ui <- navbarPage(position = "static-top",
title = "foo",
id = "tabs",
tabPanel(title = "Merge",
fluidRow(
checkboxGroupInput("to_merge",
label = "Tables to merge",
choices = NULL),
tableOutput("table")
)),
tabPanel(title = "More",
icon = icon("plus"),
fluidRow()
)
)
server <- function(input, output, session) {
count <- reactiveValues(val=0)
tables <- reactiveValues()
dfs <- list()
observeEvent(input$tabs, {
if (input$tabs == "More"){
count$val <- count$val+1
name <- paste0("Name ", count$val)
insertTab(inputId = "tabs",
tabPanel(title = name,
addTab(paste0("select", count$val))
),
target = "More",
position = "before",
select = TRUE)
x <- callModule(moduleTable, paste0("select", count$val))
tables[[name]] <- x
}
})
observe({
updateCheckboxGroupInput(session = session,
inputId = "to_merge",
choices = names(tables),
selected = input$to_merge)
})
observe({
req(input$to_merge)
output$table <- renderTable({
if(!is.null(input$to_merge)) {
tabs <- map(input$to_merge, ~{tables[[.x]]()})
reduce(tabs, full_join)
}
})
})
}
shinyApp(ui = ui, server = server)
Upvotes: 1
Reputation: 2242
UPDATE (getting a bit closer but not there yet):
I know that modules can return data (as reactiveValues
). So my challenge now is to return the data in your x()
. With some random non-reactive values, it works in the sense that I can collect (append) the output of a module call in a variable and then show these values as a string for example. Here is where I am:
library(shiny)
library(shinyWidgets)
library(dplyr)
addTab <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("select"),
"Choose",
choices = colnames(mtcars)),
tableOutput(ns("table"))
)
}
moduleTable <- function(input, output, session){
x <- reactive(select(mtcars, input$select))
output$table <- renderTable({
x()
})
table_list <- reactiveValues()
table_list$test <- sample(letters, 1)
table_list$first_value <- x()[1,1]
return(table_list)
}
ui <- navbarPage(position = "static-top",
title = "foo",
id = "tabs",
tabPanel(title = "Merge",
fluidRow(
checkboxGroupInput("to_merge",
label = "Tables to merge",
choices = NULL),
textOutput("string"),
textOutput("first_value")
)),
tabPanel(title = "More",
icon = icon("plus"),
fluidRow()
)
)
server <- function(input, output, session) {
vals <- reactiveValues(val = 0, name = "", string_output = NULL, first_value = NULL)
observeEvent(input$tabs, {
if (input$tabs == "More"){
vals$val <- vals$val+1
name <- paste0("Name ", vals$val)
insertTab(inputId = "tabs",
tabPanel(title = name,
addTab(paste0("select", vals$val))
),
target = "More",
position = "before",
select = TRUE)
m_output <- callModule(moduleTable, paste0("select", vals$val))
vals$string_output <- c(vals$string_output, m_output$test)
vals$first_value <- c(vals$first_value, m_output$first_value)
vals$name[vals$val] <- paste0("mtcars$select", vals$val, "_", m_output$test)
updateCheckboxGroupInput(session = session,
inputId = "to_merge",
choices = vals$name)
output$string <- renderText({
req(input$tabs)
paste(vals$string_output, collapse = ", ")
})
output$first_value <- renderText({
req(input$tabs)
paste(vals$first_value, collapse = ", ") # This doesn't work as expected
})
}
})
}
shinyApp(ui = ui, server = server)
How about this?
library(shiny)
library(shinyWidgets)
library(dplyr)
addTab <- function(id) {
ns <- NS(id)
tagList(
selectInput(ns("select"),
"Choose",
choices = colnames(mtcars)),
tableOutput(ns("table"))
)
}
moduleTable <- function(input, output, session){
x <- reactive(select(mtcars, input$select))
output$table <- renderTable({
x()
})
}
ui <- navbarPage(position = "static-top",
title = "foo",
id = "tabs",
tabPanel(title = "Merge",
fluidRow(
checkboxGroupInput("to_merge",
label = "Tables to merge",
choices = NULL)
)),
tabPanel(title = "More",
icon = icon("plus"),
fluidRow()
)
)
server <- function(input, output, session) {
vals <- reactiveValues(val=0, name = "")
dfs <- list()
observeEvent(input$tabs, {
if (input$tabs == "More"){
vals$val <- vals$val+1
name <- paste0("Name ", vals$val)
insertTab(inputId = "tabs",
tabPanel(title = name,
addTab(paste0("select", vals$val))
),
target = "More",
position = "before",
select = TRUE)
callModule(moduleTable, paste0("select", vals$val))
vals$name[vals$val] <- paste0("mtcars$select", vals$val)
print(vals$name)
updateCheckboxGroupInput(session = session,
inputId = "to_merge",
choices = vals$name)
}
})
}
shinyApp(ui = ui, server = server)
Upvotes: 1