Reputation: 169
I have an application that utilizes a rHandsonTableOutput
(HoT) in a module that utilizes a tabPanel
. While I can get other data from this module (e.g. selections made in a selectInput
) for some reason I get a NULL when trying to get the HoT
data. I made a very simple version of my code to show what I mean.
I've found a HoT
data is accessible if I place the module in the same UI element. For some reason, if I put it in another tab in a tabPanel
it fails to return the data. My sample code should show that here.
UPDATE - I found the module will work so long as I click on and load the tab with the problematic HoT
first! Something about "viewing" the tab first seems to fix this problem. I'd love to find a way to not need to do that. Is this related to rshiny "sessions"?
Below is the code that should reproduce the error. You'll find a simple application with two HoT
s. The main tab has one put in via module myModuleUI
. There's a second drop down tab that contains a second HoT
generated via a second module myModuleTabUI
. They're identical except that myModuleTabUI
places everything in a tabPanel
. If you press the "Add Table Button" it simply adds up the numbers in the tables but will fail when trying to do so for the HoT
with ID first_tab (i.e. the HoT
found in another tab).
This contains the main shiny app
application.R
library(shiny)
library(rhandsontable)
source('my_modules.R')
ui <- navbarPage("Module Test Tool",
tabsetPanel(id = 'mainTabset',
tabPanel("My Tab",
#This is the HoT that is accessible from a module
h4("Table 1"),
myModuleUI('my_module'),
br(),
br(),
fluidRow(
actionButton("sum_btn", "Add table data"),
br(),
br(),
textOutput('table1_sum'),
textOutput('table2_sum'),
br(),
br()
)
),
navbarMenu("My Module Tabs",
#This is the HoT that is inaccessible from a module
myModuleTabUI('first_tab', 'First')
)
)
)
server <- function(input, output, session) {
#Link logic for tab module
callModule(myModule, 'my_module')
callModule(myModuleTab, 'first_tab')
one_col = rep.int(1,3)
df = data.frame(col1 = one_col,
col2 = one_col,
col3 = one_col)
output$hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
#This button sums up all the rHandsonTable data frames
observeEvent(input$sum_btn, {
#Works just fine when not pulling from the same panel's module
module_data = callModule(getMyModuleData, 'my_module')
module_int= module_data$module_int
module_df = module_data$module_hot
output$table1_sum = renderText({
paste0("Sum of Table 1 is: ", sum(module_df)," | Integer one is: ", module_int)
})
#Fails when pulling a hands on table from another tab
module_tab_data = callModule(getMyModuleTabData, 'first_tab') #<---THIS LINE FAILS
module_tab_int= module_tab_data$module_tab_int
module_tab_df = module_tab_data$module_tab_hot
output$table2_sum = renderText({
paste0("Sum of the table in the 'First' tab is: ", sum(module_tab_df)," | Integer in 'First' tab is: ", module_tab_int)
})
})
}
## Create Shiny app ----
shinyApp(ui, server)
This file contains the modules used in this example:
my_modules.R
#Simple module containing one rHandsontable and a drop down list of integers
myModuleUI <- function(id,tab_name){
ns <- NS(id)
fluidRow(
rHandsontableOutput(ns("module_hot")),
selectInput(ns('module_int_list'),"Integers:",c(1:5), selected = 1)
)
}
#Initializes myModuleUI rHandsonTable with some values
myModule <- function(input, output, session) {
two_col = rep.int(2,3)
df = data.frame(col1 = two_col,
col2 = two_col,
col3 = two_col)
output$module_hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
}
#Returns myModule data for use outside of the module
getMyModuleData <- function(input,output,session){
return (
list(
module_hot = hot_to_r(input$module_hot),
module_int = input$module_int_list
)
)
}
#Simple module that adds the same as MyModuleUI, except in a tabPanel
myModuleTabUI <- function(id,tab_name){
ns <- NS(id)
tabPanel(tab_name,
fluidRow(
rHandsontableOutput(ns("module_tab_hot")),
selectInput(ns('module_tab_int_list'),"Integers:",c(1:5), selected = 1)
)
)
}
#Initializes myModuleTabUI rHandsonTable with some values
myModuleTab <- function(input, output, session){
three_col = rep.int(3,3)
df = data.frame(col1 = three_col,
col2 = three_col,
col3 = three_col)
output$module_tab_hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
}
#Returns MyModuleTab data for use outside of the module
getMyModuleTabData <- function(input,output,session){
return (
list(
module_tab_hot = hot_to_r(input$module_tab_hot), #<---THIS LINE FAILS
module_tab_int = input$module_tab_int_list
)
)
}
Upvotes: 0
Views: 200
Reputation: 2725
Please edit these lines:
#Fails when pulling a hands on table from another tab
module_tab_dat = callModule(getMyModuleTabData, 'first_tab') #<---THIS LINE FAILS
module_tab_int= module_tab_dat$module_tab_int
module_tab_df = module_tab_dat$module_tab_hot
You have:
module_tab_int= module_data$module_tab_int
module_tab_df = module_data$module_tab_hot
But you named the module module_tab_dat
. Copy-paste error I assume.
Update
Regarding your update please add this line in your module code:
output$module_tab_hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
# Added line
outputOptions(output, "module_tab_hot", suspendWhenHidden = FALSE)
The aptly named suspendWhenHidden
is TRUE
by default as is probably correct for most use cases. In this case it must be FALSE
. See here
Upvotes: 1