R Shiny: Change tabs from within a module
Asked Answered
S

1

2

I have a shiny app with multiple tabs, and I would like to have action buttons within the tabs that allow the user to switch tabs. I found the following pages: https://www.titanwolf.org/Network/q/e6b187b8-6cad-4ece-ad16-7ec73ed2f758/y How to switch between shiny tab panels from inside a module?, which seem to indicate that the problem is a scoping/namespace error, but they don't fully explain what is happening, and I don't have enough reputation points to comment on the other stackoverflow post asking for clarification.

Here is my sample code:

modtab1_ui <- function(id) {
  ns <- NS(id)
  tabPanel(title = 'Tab 1',
           value = NS(id, 'tab.1'),
           
           h4('This is the first tab'),
           actionButton(NS(id, 'nexttab'), 'Next Tab')
           
          )
}

modtab1_server <- function(id) {
  moduleServer(id,
               function(input, output, session) {
                 observeEvent(input$nexttab, {
                   updateTabsetPanel(session = session, inputId = NS(id, 'tabs'), selected = NS(id, 'tab.2'))
                   print('button clicked')
                 })
               })
}

modtab2_ui <- function(id) {
  ns <- NS(id)
  tabPanel(title = 'Tab 2',
           value = NS(id, 'tab.2'),
           
           h4('This is the second tab'),
          )
}


ui <- fluidPage(
  tabsetPanel(
    id = 'tabs',
    modtab1_ui('tab1'),
    modtab2_ui('tab1')
  )
)

server <- function(input, output, session) {
  modtab1_server('tab1')
}

shinyApp(ui = ui, server = server)

EDIT TO ACCOUNT FOR NEW QUESTION

modtab1_ui <- function(id) {
  ns <- NS(id)
  tabPanel(title = 'Tab 1',
           value = NS(id, 'tab.1'),

           h4('This is the first tab'),
           actionButton(NS(id, 'nexttab'), 'Next Tab'),

           textInput(NS(id,'userid'), 'User ID'),
           textOutput(NS(id, 'useridout'))
          )
}

modtab1_server <- function(id) {
  moduleServer(id,
               function(input, output, session) {
                 observeEvent(input$nexttab, {
                   updateTabsetPanel(session = session, inputId =  'tabs', selected = NS('tab2', 'tab.2'))
                   print('button clicked'),
                 })
                 
                 output$useridout <- renderText(input$userid)
               })
}

modtab2_ui <- function(id) {
  ns <- NS(id)
  tabPanel(title = 'Tab 2',
           value = NS(id, 'tab.2'),

           h4('This is the second tab'),
           actionButton(NS(id, 'firsttab'), 'First Tab'),

           textInput(NS(id, 'userid'), 'User ID'),
           textOutput(NS(id, 'useridout'))
          )
}

modtab2_server <- function(id) {
  moduleServer(id,
               function(input, output, session) {
                 observeEvent(input$firsttab, {
                   updateTabsetPanel(session = session, inputId =  'tabs', selected = NS('tab1', 'tab.1'))
                   print('button clicked'),
                 })
                 
                 output$useridout <- renderText(input$userid)
               })
}


ui <- fluidPage(
  tabsetPanel(
    id = 'tab1-tabs',
    modtab1_ui('tab1'),
    modtab2_ui('tab2')
  )
)

server <- function(input, output, session) {
  modtab1_server('tab1')
  modtab2_server('tab2')
}

shinyApp(ui = ui, server = server)

EDIT AGAIN I asked this in a new question, and it was answered: Shiny modules: switch tabs from within modules that have different namespaces

Sikata answered 25/10, 2021 at 19:14 Comment(0)
M
2

I think this is what you are looking for. Two quite small changes made! One, in the modtab1_server function, I changed the ns(id, 'tabs') to just 'tabs'. I think that since the inputId is within a module, it already adds the id, which in this case means it adds tab1. With your existing code, it would say the tabsetPanel's id is "tab1-tab1-tabs" I think, thus by removing the ns(id) it should make call the inputId as "tab1-tabs". The second change is making the tabsetPanel id to be "tab1-tabs" to encapsulate the way the module adds the "tab1" to the inputId of the updateTabsetPanel.

modtab1_ui <- function(id) {
  ns <- NS(id)
  tabPanel(title = 'Tab 1',
           value = NS(id, 'tab.1'),
           
           h4('This is the first tab'),
           actionButton(NS(id, 'nexttab'), 'Next Tab')
           
  )
}

modtab1_server <- function(id) {
  moduleServer(id,
               function(input, output, session) {
                 observeEvent(input$nexttab, {
                   updateTabsetPanel(session = session, inputId = 'tabs', selected = NS(id, 'tab.2'))
                   print('button clicked')
                 })
               })
}

modtab2_ui <- function(id) {
  ns <- NS(id)
  tabPanel(title = 'Tab 2',
           value = NS(id, 'tab.2'),
           
           h4('This is the second tab'),
  )
}


ui <- fluidPage(
  tabsetPanel(
    id = 'tab1-tabs',
    modtab1_ui('tab1'),
    modtab2_ui('tab1')
  )
)

server <- function(input, output, session) {
  modtab1_server('tab1')
}

shinyApp(ui = ui, server = server)
Muntin answered 25/10, 2021 at 19:34 Comment(2)
how would I handle this if I wanted to use different IDs in the different modules? For example, if I wanted to have an input$userid in each module ui, I think I would need to call modtab1_ui('tab1'), modtab2_ui('tab2'). But this causes problems when switching tabs. If I add an actionButton to modtab2 that switches back to tab1, using different ids (tab1 and tab2) lets me go from 1 to 2, but not from 2 back to 1. I edited my initial question to include your suggested changes and account for what I'm trying to do now.Sikata
Just kidding, I asked a new question (#69832050) and it was answered thereSikata

© 2022 - 2024 — McMap. All rights reserved.