Reputation: 10375
While trying to come up with an overly complicated solution for this question, I stumbled across the following problem. I have 2 different modules, inputtable
displays an rhandsontable
and returns this table, and resulttable
takes this table as the input, performs some calculation and displays the result as an rhandsontable
. inputtable
returns the rhandsontable
object as a reactive value. Because there can be several copies of each module, I store the results from inputtable
in a list and feed the list elements as the input to resulttable
.
I've noticed that in resulttable
, the reactive input input_data$input_table()
can't directly be used. When I call a browser
or print
function or assign it to a variable before I use the reactive value for the actual purpose, then it works. Otherwise I get the error
attempt to apply non-function
As far as I understood passing reactive values to modules, this should work without the need to do something else to the reactive value before using it. If I don't use a list to store the reactive value, but only using one copy of each module and directly storing the result of inputtable
in a variable and passing this to resulttable
, it works as I would expect. (But storing the different reactive values in a reactiveValues
object also leads to the error.)
Does someone know what is going on there?
I apologise for the long example, when I tried to shorten it I lost the error:
library(shiny)
library(rhandsontable)
# define the first module
resulttableUI <- function(id) {
ns <- NS(id)
tabPanel(title = ns(id),
column(12,
rHandsontableOutput(ns("results_table"))))
}
resulttable <- function(id, input_data) {
moduleServer(
id,
function(input, output, session) {
# THE NEXT LINE NEEDS TO BE UNCOMMENTED TO MAKE IT WORK
# used_data <- input_data$input_table()
output$results_table <- renderRHandsontable({
rhandsontable(hot_to_r(input_data$input_table())[2:5]/hot_to_r(input_data$input_table())[1:4])
})
}
)
}
# define the second module
inputtableUI <- function(id) {
ns <- NS(id)
tabPanel(title = ns(id),
column(12,
rHandsontableOutput(ns("input_table"))))
}
inputtable <- function(id, i) {
moduleServer(
id,
function(input, output, session) {
output$input_table <- renderRHandsontable({
mat <- matrix(c(1:25) * i, ncol = 5, nrow = 5)
mat <- as.data.frame(mat)
rhandsontable(mat)
})
return(list(
input_table = reactive({input$input_table})
))
}
)
}
ui <- navbarPage("App",
tabPanel("Input",
numericInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
tabsetPanel(id = "insert_input")),
tabPanel("Results",
tabsetPanel(id = "insert_results"))
)
number_modules <- 0
current_id <- 1
server <- function(input, output, session) {
# variable to store the inputs from the modules
input_data <- list()
observeEvent(input$num_of_table, {
modules_to_add <- input$num_of_table - number_modules
for (i in seq_len(modules_to_add)) {
# add the logic for the input
input_data[[paste0("inputtable_", current_id)]] <<-
inputtable(paste0("inputtable_", current_id), current_id)
# add the logic for the results
resulttable(paste0("resulttable_", current_id),
input_data = input_data[[paste0("inputtable_", current_id)]])
# add the UI
appendTab(inputId = "insert_input",
tab = inputtableUI(paste0("inputtable_", current_id)))
appendTab(inputId = "insert_results",
tab = resulttableUI(paste0("resulttable_", current_id)))
# update the id
current_id <<- current_id + 1
}
number_modules <<- input$num_of_table
updateTabsetPanel(session,
"insert_input",
"inputtable_1-inputtable_1")
})
}
shinyApp(ui,server)
I'm using R 3.6.1
and shiny 1.5.0
.
Unfortunately, there are 2 other issues:
inputtable
module uses i = 2
instead of i = 1
, I haven't figured out yet why.So maybe there is something else wrong with my code. I'm glad for any hints for this strange behaviour or how to make a more minimal example.
Upvotes: 2
Views: 442
Reputation: 21349
By changing for
loop to lapply
, and some other minor modifications in server function, I think it works. Try this
ui <- fluidPage(navbarPage("App",
tabPanel("Input",
sliderInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
#numericInput('num_of_table', "Number of sub tabs: ", value = 1, min = 1, max = 10),
tabsetPanel(id = "insert_input")),
tabPanel("Results",
tabsetPanel(id = "insert_results"))
))
#number_modules <- 0
current_id <- 0
server <- function(input, output, session) {
number_modules <- reactiveVal(0)
# variable to store the inputs from the modules
input_data <- list()
observeEvent(input$num_of_table, {
req(input$num_of_table)
if (input$num_of_table > number_modules() ){
modules_to_add <- reactive({input$num_of_table - number_modules()})
}else {
modules_to_add <- reactive({0})
}
lapply(1:modules_to_add(), function(i) {
# update the id
current_id <<- current_id + 1
input_data[[paste0("inputtable_", current_id)]] <<-
inputtable(paste0("inputtable_", current_id), current_id)
# add the logic for the results
resulttable(paste0("resulttable_", current_id),
input_data = input_data[[paste0("inputtable_", current_id)]])
## add the UI
if (input$num_of_table > number_modules() ){
appendTab(inputId = "insert_input",
tab = inputtableUI(paste0("inputtable_", current_id)))
appendTab(inputId = "insert_results",
tab = resulttableUI(paste0("resulttable_", current_id)))
}
})
if (input$num_of_table > number_modules() ){
number_modules(input$num_of_table)
updateTabsetPanel(session,
"insert_input",
"inputtable_1-inputtable_1")
}
})
}
It may still need an update on what to display as input table displays the last table for all sub-tabs if a high number is chosen for num_of_table.
Upvotes: 2