Search code examples
rshinynamespacestoggleshinyjs

Shiny namespace issue with toggle and tabsetPanel


I’m looking for some help with a simple Shiny app with a modularised design please. I think the problem is a name space issue so the example below is set out as a simplified version of my actual project.

The aim is for ‘tab_3’ on the tabsetPanel to only show when the ‘View Tab_3’ is checked, which works fine. I would like to update the tabsetPanel however to also select ‘tab_3’ when ‘View Tab_3’ is checked and this is not firing as desired.

I can get the tabsetPanel to also select ‘tab_3’ when ‘View Tab_3’ is checked if I wrap the tabsetPanel’s id in a namespace function, id = ns("tab_a_tha"), however then I lose the show/hide functionality of ‘tab_3’.

My hunch is that the solution lies within providing a namespace to the toggle function but I haven’t found any clues on how to approach it.

library(shiny)
library(shinyjs)

inner_moduleUI <- function(id){
  ns <- NS(id)
  tagList(
    fluidRow(checkboxInput(ns("chckbx"), "View Tab_3", value = F)),
    tabsetPanel(
      id = "tab_a_tha",
      # id = ns("tab_a_tha"),
      tabPanel('tab_1'),
      tabPanel('tab_2'),
      tabPanel('tab_3')
    )
  )
}



inner_module <- function(input, output, session){
  
  observeEvent(input$chckbx, {
    
    toggle(condition = input$chckbx, selector = "#tab_a_tha li a[data-value=tab_3]")
    
    if(input$chckbx == T){
      updateTabsetPanel(session, 'tab_a_tha', selected = 'tab_3')
    }

  })
  
}

ui <- fluidPage(
  useShinyjs(),
  uiOutput('main_ui')
)

server <- function(input, output, session) {
  
  output$main_ui <- renderUI({inner_moduleUI('inner_ns')  })
  callModule(inner_module, 'inner_ns')
  
}

shinyApp(ui = ui, server = server)

Solution

  • you are right the problem is with the namespace. The trick is that you can access the namespace function also in the server part of a module with session$ns.

    Using this and wrapping the tap id in the ns function. We can use paste0 to generate the new selector of the toggle function. We get something like this:

    library(shiny)
    library(shinyjs)
    
    inner_moduleUI <- function(id){
      ns <- NS(id)
      tagList(
        fluidRow(checkboxInput(ns("chckbx"), "View Tab_3", value = F)),
        tabsetPanel(
          id = ns("tab_a_tha"),
          # id = ns("tab_a_tha"),
          tabPanel('tab_1'),
          tabPanel('tab_2'),
          tabPanel('tab_3')
        )
      )
    }
    
    
    
    inner_module <- function(input, output, session){
      
      observeEvent(input$chckbx, {
        toggle(condition = input$chckbx, selector = paste0("#",session$ns("tab_a_tha")," li a[data-value=tab_3]"))
        
        if(input$chckbx == T){
          updateTabsetPanel(session, 'tab_a_tha', selected = 'tab_3')
        }
        
      })
      
    }
    
    ui <- fluidPage(
      useShinyjs(),
      uiOutput('main_ui')
    )
    
    server <- function(input, output, session) {
      
      output$main_ui <- renderUI({inner_moduleUI('inner_ns')  })
      callModule(inner_module, 'inner_ns')
      
    }
    
    shinyApp(ui = ui, server = server)