Sooji
Sooji

Reputation: 169

rHandsonTable module returning NULL when called from another tabPanel

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 HoTs. 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

Answers (1)

Eli Berkow
Eli Berkow

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

Related Questions