Generic button for go to next and previous tabItem Shiny - r

I know this is pretty close to previously aked questions, but after thorough study of those examples I haven't found a solution for my particular problemm yet.
I have a shiny App using Shiny Dashboard with this structure (*1). I can make a next or previous page button this way:
next_btn <- actionButton( inputId ="Next1",
label = icon("arrow-right"))
with an observer :
observeEvent(input$Next1, {
updateTabItems(session, "tabs", "NAME")
})
where NAME is the tabItem ID. This version is simpler than the expamples I've found that use switch and or simply Navigate to particular sidebar menu item in ShinyDashboard?
However, this only works to switch from pagename1 to pagename2 with a specific button for it.
I have however, 10-20 tabItems in my app : ** <<- the reason for my problem**
The approach mentioned about would require me to write a actionbutton(next1, ... ac but next 2 , next 3 etc. 1 for each page, and also an separate observer for each.
What I am trying to make is this:
1 generic action button called "NEXTPAGE"
with an observer that does updateTabItems(session, tabs, "current page +1"
to to the current page +1 in whatever way I'm lost. I could imagine making a list parameter of all tab names, find the current tabname in that list, grab it's position, shift one position up (previous), or down (next) for example.
However, I do not know how to get a list variable of all tabItems present in my app, other than some very laborious manual typing of a list of strings.
*1 app structure:
library(shiny)
library(shinydashboard)
### create general button here like:
### write a function that looks at what (nth) tabItem we are, and creates a ### uiOutput for a next_n button (I can do this myself I think)
dashboardHeader(title = "FLOW C.A.R.S."),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Home", tabName = "Home", icon = icon("home")),
menuItem("My Page", tabName = "MyPage", icon =icon("download")),
menuItem("Do math", tabName = "Math", icon=icon("folder-open")),
menuItem("Results of something", tabName="Results", icon=
icon("file-text-o")),
menuItem("Short Manual", tabName = "Manual", icon = icon("book"))
)
),
dashboardBody(
tabItems(
tabItem(tabName = "Home", class = 'rightAlign',
actionButton( inputId ="Next1", label = icon("arrow-right"))),
tabItem(tabName = "MyPage", class = 'rightAlign',
actionButton( inputId ="Next2", label = icon("arrow-right")),
actionButton( inputId ="Previous2", label = icon("arrow-left"))),
tabItem(tabName = "Math", class = 'rightAlign',
actionButton( inputId ="Next3", label = icon("arrow-right")),
actionButton( inputId ="Previous3", label = icon("arrow-left"))),
tabItem(tabName = "tabName", class = 'rightAlign',
actionButton( inputId ="Next4", label = icon("arrow-right")),
actionButton( inputId ="Previous4", label = icon("arrow-left"))),
tabItem(tabName = "Maual", class = 'rightAlign',
actionButton( inputId ="Previous5", label = icon("arrow-left")))
))
server:
shinyServer = function(input, output, session) {
observeEvent(input$Next1, {
updateTabItems(session, "tabs", "MyPage)
})
observeEvent(input$Previous2, {
updateTabItems(session, "tabs", "Home")
})
observeEvent(input$Next2, {
updateTabItems(session, "tabs", "Math)
})
### repeat for next2 and previous 2 , 3 etc
}
Summary, I'm looking for a code that will give us the name of the Tab coming after of before the current tab, so that we can stuff the outcome of that query into updateTabItems(session, "tabs" .......)
so that we can make a more general observer that says for instance;
if Next[i] button is clicked go to tabItem[i+1]
but like I said, I can imagine myself writing such a code, if only if I knew how to access the list of tabItems with function (obviously I have the names in the ui page since I labelled all of them, but I'm trying to avoid all the redunant repetition of code by typing it all out for each page/button/observer)
only thing I discoverd so far is that paste(input$tabs) inside an observer will give you the current tab, but then what...
thanks for anny help!
If it's unclear, please feel free to contact me

I will admit that this is not fully generalized. It requires that you place a vector in your server that has the names of the tabs from the UI. But, you really only need two buttons to make it work (not two buttons per tab). You only need to make sure that the tab_id vector has the correct names in the same order as the UI. You can probably get away with something like this if it is a small scale project where the tabs and tab names are not changing a lot.
library(shiny)
library(shinydashboard)
library(shinyjs)
### create general button here like:
### write a function that looks at what (nth) tabItem we are, and creates a ### uiOutput for a next_n button (I can do this myself I think)
shinyApp(
ui =
dashboardPage(
dashboardHeader(title = "FLOW C.A.R.S."),
dashboardSidebar(
useShinyjs(),
sidebarMenu(id = "tabs",
menuItem("Home", tabName = "Home", icon = icon("home")),
menuItem("My Page", tabName = "MyPage", icon =icon("download")),
menuItem("Do math", tabName = "Math", icon=icon("folder-open")),
menuItem("Results of something", tabName="Results", icon=
icon("file-text-o")),
menuItem("Short Manual", tabName = "Manual", icon = icon("book"))
)
),
dashboardBody(
hidden(actionButton(inputId ="Previous", label = icon("arrow-left"))),
hidden(actionButton(inputId ="Next", label = icon("arrow-right")))
)
),
server =
shinyServer(function(input, output, session){
tab_id <- c("MyPage", "Math", "Results", "Manual")
observe({
lapply(c("Next", "Previous"),
toggle,
condition = input[["tabs"]] != "Home")
})
Current <- reactiveValues(
Tab = "Home"
)
observeEvent(
input[["tabs"]],
{
Current$Tab <- input[["tabs"]]
}
)
observeEvent(
input[["Previous"]],
{
tab_id_position <- match(Current$Tab, tab_id) - 1
if (tab_id_position == 0) tab_id_position <- length(tab_id)
Current$Tab <- tab_id[tab_id_position]
updateTabItems(session, "tabs", tab_id[tab_id_position])
}
)
observeEvent(
input[["Next"]],
{
tab_id_position <- match(Current$Tab, tab_id) + 1
if (tab_id_position > length(tab_id)) tab_id_position <- 1
Current$Tab <- tab_id[tab_id_position]
updateTabItems(session, "tabs", tab_id[tab_id_position])
}
)
})
)

As i wrote in the comment:
The easiest would be for sure to rewrite the code and have an array: tabItemNames = c("Home", "MyPage",....) and then name the tabs accordingly tabItem(tabName = tabItemNames[1],...), tabItem(tabName = tabItemNames[2],... etc. That i wouldnt call redundant repition of code,...(see also Benjamin´s answer.
However, I appreciated the JS challenge and gave it a shot:
You could use JS to read the tabItemNames. That would fulfill the bonus requirement of not having to hardcode them in the code.
observe({
runjs("
function getAllElementsWithAttribute(attribute){
var matchingElements = [];
var allElements = document.getElementsByTagName('*');
for (var i = 0, n = allElements.length; i < n; i++){
if (allElements[i].getAttribute(attribute) !== null){
matchingElements.push(allElements[i]);
}
}
return matchingElements;
};
ahref = getAllElementsWithAttribute('data-toggle');
var tabNames = [];
var tabName = '';
for (var nr = 0, n = ahref.length; nr < n; nr++){
tabName = ahref[nr].hash.split('-')[2]
if(tabName != 'Toggle navigation') tabNames.push(tabName)
}
Shiny.onInputChange('tabNames', tabNames);
")
})
The assumption i make that you do not have any further element having a 'data-toggle' attribute. If this would not be fulfilled one would have to integrate further conditions in the code.
In the following a running example, build by the code above combined with the code provided by Benjamin:
library(shiny)
library(shinydashboard)
library(shinyjs)
app <- shinyApp(
ui =
dashboardPage(
dashboardHeader(title = "FLOW C.A.R.S."),
dashboardSidebar(
useShinyjs(),
sidebarMenu(id = "tabs",
menuItem("Home", tabName = "Home", icon = icon("home")),
menuItem("My Page", tabName = "MyPage", icon =icon("download")),
menuItem("Do math", tabName = "Math", icon=icon("folder-open")),
menuItem("Results of something", tabName="Results", icon=
icon("file-text-o")),
menuItem("Short Manual", tabName = "Manual", icon = icon("book"))
)
),
dashboardBody(
actionButton(inputId ="Previous", label = icon("arrow-left")),
actionButton(inputId ="Next", label = icon("arrow-right"))
)
),
server =
shinyServer(function(input, output, session){
global <- reactiveValues(tab_id = "")
tab_id <- c("Home", "MyPage", "Math", "Results", "Manual")
Current <- reactiveValues(
Tab = "Home"
)
observeEvent(
input[["tabs"]],
{
Current$Tab <- input[["tabs"]]
}
)
observeEvent(
input[["Previous"]],
{
tab_id_position <- match(Current$Tab, input$tabNames) - 1
if (tab_id_position == 0) tab_id_position <- length(input$tabNames)
Current$Tab <- input$tabNames[tab_id_position]
updateTabItems(session, "tabs", input$tabNames[tab_id_position])
}
)
observeEvent(
input[["Next"]],
{
tab_id_position <- match(Current$Tab, input$tabNames) + 1
if (tab_id_position > length(input$tabNames)) tab_id_position <- 1
Current$Tab <- input$tabNames[tab_id_position]
updateTabItems(session, "tabs", input$tabNames[tab_id_position])
}
)
observe({
runjs("
function getAllElementsWithAttribute(attribute){
var matchingElements = [];
var allElements = document.getElementsByTagName('*');
for (var i = 0, n = allElements.length; i < n; i++){
if (allElements[i].getAttribute(attribute) !== null){
matchingElements.push(allElements[i]);
}
}
return matchingElements;
};
ahref = getAllElementsWithAttribute('data-toggle');
var tabNames = [];
var tabName = '';
for (var nr = 0, n = ahref.length; nr < n; nr++){
tabName = ahref[nr].hash.split('-')[2]
if(tabName != 'Toggle navigation') tabNames.push(tabName)
}
Shiny.onInputChange('tabNames', tabNames);
")
})
})
)
runApp(app, launch.browser = TRUE)
The javascript function to read the elements I used from here: Get elements by attribute when querySelectorAll is not available without using libraries?

Related

Generate dynamic Tab using argonDash (shiny framework) package

I tried to make dynamic Tab using argonDash package. However, I met some trouble with my code.
I guess argonDash seems to get different behavior than native shiny because argonDash uses Bootstrap 4 (instead of 3).
My code is composed of two key features:
The first widget uiOutput(outputId = "new_argonSidebarItem") works well.
The second widget uiOutput(outputId = "new_argonTabItem") doesn't work properly. I expect that the argonBadge() toggle with the second tab.
library(shiny)
library(argonDash)
library(argonR)
ui <- argonDashPage(
sidebar = argonDashSidebar(
id = "sidebar01",
vertical = FALSE,
size = "md",
background = "white",
argonSidebarMenu(
style = "display:-webkit-inline-box;",
argonSidebarItem(
tabName = "Tab1",
icon = NULL,
"Dashboard"
),
# New Tab
uiOutput(outputId = "new_argonSidebarItem")
)
),
body = argonDashBody(
argonTabItems(
argonTabItem(
tabName = "Tab1",
actionButton("add_more", "Add a new btn")
),
# Body
uiOutput(outputId = "new_argonTabItem")
)
)
)
server <- function(input, output, session) {
observeEvent(input$add_more, {
tabId <- sample.int(n = 1000000, size = 1)
tabName <- paste0("Tab_", tabId)
insertUI(
selector = "#tab-Tab1",
where = "afterEnd",
ui = argonSidebarItem(
tabName = tabName,
tabName
)
)
output$new_argonTabItem <- renderUI({
argonTabItem(
tabName = tabName,
argonBadge(
text = tabName,
src = "#",
pill = FALSE,
status = "success"
)
)
})
})
}
shinyApp(ui, server)
Thank you for your time!
To update your badge you need to track which tab is selected. This is done accordingly to this solution : https://github.com/RinteRface/argonDash/issues/7
But your badge can not be modified, so I replaced it with a button from {shinyWidgets}.
I hope this is what you ask for, I'm not really sure to understand what you want to happen.
library(shiny)
library(argonDash)
library(argonR)
library(shinyWidgets)
ui <- argonDashPage(
sidebar = argonDashSidebar(
id = "sidebar01",
vertical = FALSE,
size = "md",
background = "white",
argonSidebarMenu(
style = "display:-webkit-inline-box;",
argonSidebarItem(
tabName = "Tab1",
icon = NULL,
"Dashboard"
),
# New Tab
uiOutput(outputId = "new_argonSidebarItem")
)
),
body = argonDashBody(
argonTabItems(
argonTabItem(
tabName = "Tab1",
actionButton("add_more", "Add a new btn")
),
# Body
uiOutput(outputId = "new_argonTabItem")
),
tags$script( "$(document).on('click', function(event) {
Shiny.onInputChange('activeTab', $('.active').data().value);});")
)
)
server <- function(input, output, session) {
x <- reactiveValues(tabs = NULL)
observeEvent(input$add_more, {
tabId <- sample.int(n = 1000000, size = 1)
tabName <- paste0("Tab_", tabId)
insertUI(
selector = "#tab-Tab1",
where = "afterEnd",
ui = argonSidebarItem(
tabName = tabName,
tabName
)
)
output$new_argonTabItem <- renderUI({
argonTabItem(
tabName = tabName,
actionBttn(
inputId = paste0(input$activeTab,"_b"),
label = tabName,
style = "float",
color = "success"
)#,
# argonBadge(
# text = tabName,
# src = "#",
# pill = FALSE,
# status = "success"
# )
)
})
})
observeEvent(input$activeTab, {
updateActionButton(session,
paste0(input$activeTab,"_b"),
input$activeTab)
})
}
shinyApp(ui, server)

Update Controlbar in bs4dash version 2.00 Shiny

I have moved over to the new bs4dash and I am having some issues with updating the control bar.
For each different tab on my sidebar i would like a corresponding control bar. For example, if the sidebar tab is "Home", I would like to the controlbar to consist of multiple selectizeInputs. If however, the sidebar tab is "News", I would like the Control Bar to have different textOutputs.
Here is some of the code I am using
##UI
controlbar = dashboardControlbar(
id = "controlbar",
collapsed = T
)
##Server
observeEvent(input$current_tab,{
if(input$current_tab == "home"){
updateControlbar(id = "controlbar", session = session,
selectizeInput("one", "one", choices = c(1,2,3)
),
selectizeIntput("two", "two", choices = c(1,2,3)
} else if(input$current_tab == "News"){
updateControlbar(id = "controlbar", session = session,
textInput("news1"),
textInput("news2")
}
})
I have also tried many other combos but nothing seems to work.
Thank you for your help
You can combine conditional panels with reactive functions. This code snippet show a very trivial case.
library(shiny)
library(bs4Dash)
shinyApp(
ui = dashboardPage(
header = dashboardHeader(),
sidebar = dashboardSidebar(uiOutput("sidebar")),
body = dashboardBody(),
controlbar = dashboardControlbar(uiOutput("controlbar"))
),
server = function(input, output, session) {
output$sidebar <- renderMenu({
sidebarMenu(id = "main_menu",
menuItem(text = "First page", tabName = "tab1"),
menuItem(text = "Second page", tabName = "tab2")
)
})
output$show_tab1 <- reactive({
!is.null(input$main_menu) && input$main_menu == "tab1"
})
output$show_tab2 <- reactive({
!is.null(input$main_menu) && input$main_menu == "tab2"
})
outputOptions(output, "show_tab1", suspendWhenHidden = FALSE)
outputOptions(output, "show_tab2", suspendWhenHidden = FALSE)
output$controlbar <- renderUI({
div(
conditionalPanel(
condition = "output.show_tab1",
p("Widgets for the first page")
),
conditionalPanel(
condition = "output.show_tab2",
p("Widgets for the second page")
)
)
})
}
)

How to make a tabitem to be rendered only after the user clicks it - shiny R

I´ve been looking for the solution to this but I do not find it
My issue is that I have a shiny dashboard that looks like this:
It is selecting all tabs even If I do not select them (like pre-rendered)
I tried making an observeEvent with a button but It do not know how to make the UI appear after they click it.
My code is
library(shiny)
library(shinydashboard)
gamestop <- tags$img(src = "GSLL.png",
height = '30', width = '170')
ui <- dashboardPage(skin = "yellow",
dashboardHeader(title = gamestop,
dropdownMenu(type = "tasks",
messageItem(
from = "My contact",
message = "x",
icon = icon("glyphicon glyphicon-envelope"),
href = "mailto:x"),
messageItem(
from = "Leads",
message = "y",
icon = icon("glyphicon glyphicon-envelope"),
href = "mailto:y"),
messageItem(
from = "",
message = "z",
icon = icon("glyphicon glyphicon-envelope"),
href = "mailto:z"),
icon = icon("envelope")
)
),
dashboardSidebar(
sidebarMenu(
menuItem("Main menu", tabName = "main_menu", icon = icon("home")),
menuItem("Peripherals", tabName = "peripherals", icon = icon("hdd")),
menuItem("Database repair", tabName = "widgets", icon = icon("th")),
menuItem("Polling", tabName = "polling", icon = icon("cloud")),
menuItem("more issues!!", tabName = "issues", icon = icon("ad"))
)
),
dashboardBody(tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "Custom.css")),
fluidPage(
tabItem(tabName = "peripherals",
h1("Peripherals",
style = "color:#15942B"),
tabsetPanel(type = "tabs",
tabPanel("Printers",br(),
tabsetPanel(type = "tabs",
tabPanel("M452DW",br(),
tabsetPanel(type = "tabs",
tabPanel("Not pinging"),
tabPanel("Error messages")
)
),
tabPanel("M402DW",br(),
tabsetPanel(type = "tabs",
tabPanel("Not pinging")
),
)
)
),
tabPanel("Pinpad",br(),
tabsetPanel(type = "tabs",
tabPanel("Offline / busy"),
tabPanel("Not turning on")
)
),
tabPanel("Scanners",br(),
tabsetPanel(type = "tabs",
tabPanel("GBT4400"),
tabPanel("DS2278")
)
),
tabPanel("Receipt printer / cashdrawer",br(),
tabsetPanel(type = "tabs",
tabPanel("Receipt printer"),
tabPanel("Cash drawer")
)
),
tabPanel("Label printer",br(),
tabsetPanel(type ="tabs",
tabPanel("ZD410"),
tabPanel("LP2824 & +")
),
)
)
), #Final tab peripherals
tabItem(tabName = "main_menu",
h1("Main menu",
style = "color:#15942B"),
strong("Here we can add the news of the day or a welcome image"),br(),
br(),
br(),
strong("This is a work in progress, to be presented to our team leads so we can make
it an aid page for all of us")
),
tabItem(tabName = "issues",
h1("More issue resolutions to come!!!!!",
style = "color:#15942B" ),
strong("My plan is to add the hardest issue resolutions for our team, so they can access this web page and
with a glipse they can resolve the issue in hand")
),
tabItem(tabName = "polling",
)
)
)
)
)
server <- function(input, output) {
observeEvent(input$tabs,
if(input$sidebarmenu == "Printers"){
})
}
shinyApp(ui, server)
I would like to know how to render the tab when the user clicks on the tab itself and not before
Thanks a lot!!!
If you want to render the tab when the user clicks on the tab, you need to observe the tabsetpanel and check if the tab is clicked.
library(shiny)
library(shinydashboard)
ui <- fluidPage(
tabItem(tabName = "peripherals",
h1("Peripherals",
style = "color:#15942B"),
tabsetPanel(id = "firsttabset",
type = "tabs",
tabPanel("Printers"),
tabPanel("Pinpad1",
h1("Dies ist tab \"Pinpad1\""),
textOutput("text")),
tabPanel("Scanners"),
tabPanel("Receipt printer / cashdrawer"),
tabPanel("Label printer")
)
)
)
server <- function(input, output) {
observeEvent(input$firsttabset, {
if(input$firsttabset == "Pinpad1") {
cat("tab \"Pinpad1\" is now being rendered \n")
output$text <- renderText({"tadaa"})
}
})
}
shinyApp(ui, server)
EDIT: If you want to apply this to nested tabsetpanels, I found a way by observing both tabsetpanel1 and tabsetpanel2 and checking in the conditions which tabs are selected. I suppose the first tab of tabsetpanel2, that is Scanners2 in this example, has to be rendered if you want to render the tab Pinpad1.
Check it out if it works for you. This logic can be extended to further nesting of tabsetpanels, but it will get complicated.
library(shiny)
library(shinydashboard)
ui <- fluidPage(
tabItem(tabName = "peripherals",
h1("Peripherals",
style = "color:#15942B"),
tabsetPanel(id = "firsttabset",
type = "tabs",
tabPanel("Printers"),
tabPanel("Pinpad1",
tabsetPanel(id = "secondtabset",
type = "tabs",
tabPanel("Scanners2",
h1("Dies ist tab \"Scanners2\"")),
tabPanel("Pinpad2",
h1("Dies ist tab \"Pinpad2\""),
textOutput("text2"))),
h1("Dies ist tab \"Pinpad1\""),
textOutput("text")),
tabPanel("Scanners"),
tabPanel("Receipt printer / cashdrawer"),
tabPanel("Label printer")
)
)
)
server <- function(input, output) {
observeEvent(c(input$firsttabset,
input$secondtabset), {
if(input$firsttabset == "Pinpad1" & input$secondtabset == "Scanners2") {
cat("tab \"Pinpad1\" is now being rendered \n")
output$text <- renderText({"tadaa"})
}
if (input$firsttabset == "Pinpad1" & input$secondtabset == "Pinpad2") {
cat("tab2 \"Pinpad2\" is now being rendered \n")
output$text2 <- renderText({"tadooo"})
}
})
}
shinyApp(ui, server)

Update Shiny input to match other Shiny input: infinite loop

I would like to have two instances of an input controller in my Shiny app, but I think that what I have to do instead is to have two inputs and update the value of each whenever the other changes. This way, they will appear to the user to be the same controls despite the fact that they have different IDs.
I anticipate being told to not do what I am trying to do, but the use case is that I have many tabs in a dashboardPage(), and only two of them share controls. Thus, putting the controls for those two pages in the sidebar would be confusing to the user.
I made a simple, working example of how to do this (using a dashboard to make it more clear why I want to do this) based on a closely-related question that was answered by convincing the asker to do something else (which worked in their case but not in mine). The app works fine except that as it gets more and more complex, the calculations take long enough sometimes that I can change one input and then change the other before the Shiny server has had time to update the values. This results in infinite feedback (input 1 updates to match input 2 while input 2 is updating to match input 1, and then this repeats for as long as I care to watch).
library(shiny)
library(shinydashboard)
ui = dashboardPage(
dashboardHeader(title = "Example"),
dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = "tab1", icon = icon("chart-line")),
menuItem("Tab 2", tabName = "tab2", icon = icon("chart-line")),
menuItem("Other Tab", tabName = "tab3", icon = icon("project-diagram"))
)
),
dashboardBody(
tabItems(
# First tab content
tabItem(tabName = "tab1",
# Input first number
numericInput("input1", label = "Input 1", value = 1, min=1, step=1)
),
# Second tab content
tabItem(tabName = "tab2",
# Input second number
numericInput("input2", label = "Input 2", value = 1, min=1, step=1)
),
# Third tab content
tabItem(tabName = "tab3", "Unrelated content")
)
)
)
server = function(input, output, session) {
# Update inputs to match each other
observeEvent(input$input1, {
updateSelectInput(session = session,
inputId = "input2",
selected = input$input1)})
observeEvent(input$input2, {
updateSelectInput(session = session,
inputId = "input1",
selected = input$input2)})
}
shinyApp(ui = ui, server = server)
The question: what other ways are there to have separate pages with matching controls that control both pages but without having to put those controls on every page? Sub-question: is any of these methods going to avoid the infinite loop problem? Corollary: I saw an article that I think was rendering UI pages from auxiliary scripts and passing the input arguments to the URLs for those scripts, and that seemed like a great strategy, but I cannot find the article now and am struggling to figure it out on my own.
It is much simpler in fact. Instead of observing the numeric inputs, you can observe what tab is selected, and update a particular numericInput when the user arrives at that tab. So all we need is to provide an id for the sidebarMenu (id = "tabs", ...) and to observe the contents of this input variable:
observe({
if (req(input$tabs) == "tab2") {
updateSelectInput(...)
}
})
Changing input values with keyboard:
Changing input values with mouse clicking on up arrow:
Changing to tab2 while tab1 is rendering though the list of clicks:
Updated code:
library(shiny)
library(shinydashboard)
ui = dashboardPage(
dashboardHeader(title = "Example"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Tab 1", tabName = "tab1", icon = icon("chart-line")),
menuItem("Tab 2", tabName = "tab2", icon = icon("chart-line")),
menuItem("Other Tab", tabName = "tab3", icon = icon("project-diagram"))
)
),
dashboardBody(
tabItems(
# First tab content
tabItem(tabName = "tab1",
# Input first number
numericInput("input1", label = "Input 1", value = 1000, min=1, step=1),
plotOutput("plot1")
),
# Second tab content
tabItem(tabName = "tab2",
# Input second number
numericInput("input2", label = "Input 2", value = 1000, min=1, step=1),
plotOutput("plot2")
),
# Third tab content
tabItem(tabName = "tab3", "Unrelated content")
)
)
)
server = function(input, output, session) {
# some (not so) long computation
long_comp1 <- reactive({
x <- sample(input$input1, size=10000000, replace = TRUE)
y <- sample(input$input1, size=10000000, replace = TRUE)
m <- matrix(x, nrow = 500, ncol=200)
n <- matrix(y, nrow = 200, ncol=500)
p <- n %*% m
p
})
output$plot1 <- renderPlot({
hist(long_comp1(), main = paste("input1 is", input$input1))
})
# some (not so) long computation
long_comp2 <- reactive({
x <- sample(input$input2, size=10000000, replace = TRUE)
y <- sample(input$input2, size=10000000, replace = TRUE)
m <- matrix(x, nrow = 500, ncol=200)
n <- matrix(y, nrow = 200, ncol=500)
p <- n %*% m
p
})
output$plot2 <- renderPlot({
hist(long_comp2(), main = paste("input2 is", input$input2))
})
# Update inputs to match each other
observe({
if (req(input$tabs) == "tab2") {
updateSelectInput(session = session,
inputId = "input2",
selected = input$input1)
}
})
observe({
if (req(input$tabs) == "tab1") {
updateSelectInput(session = session,
inputId = "input1",
selected = input$input2)
}
})
}
shinyApp(ui = ui, server = server)

updateTabItems not working in module if input passed

I want to create a function/a module to update tabset items.
I'm aware of this and this question, but my issue relates to how the button input is handled in updateTabItems.
Here you can find an example:
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
mod_updateTabset <- function(
input, output, session, triggerId, dashboardId, tab, parent
) {
observeEvent(triggerId, {
updateTabItems(parent, dashboardId, selected = tab)
})
}
ui <- dashboardPagePlus(
header = dashboardHeaderPlus(),
sidebar = dashboardSidebar(
sidebarMenu(
id = 'Tabs',
menuItem("Tab 01", tabName = "tab01", icon = icon("dice-one")),
menuItem("Tab 02", tabName = "tab02", icon = icon("dice-two"))
)
),
body = dashboardBody(
tabItems(
tabItem(
tabName = "tab01",
actionButton("updateButton", label = "To Tab02")
),
tabItem(
tabName = "tab02",
h4("New Tab")
)
)
)
)
server <- function(input, output, session) {
callModule(
mod_updateTabset,
"updateLink",
triggerId = input$updateButton,
dashboardId = "Tabs",
tab = "tab02",
parent = session
)
}
shinyApp(ui = ui, server = server)
I know it's working when creating mod_updateTabset_UI, shifting the actionButton to the module. That's why I suppose the issue lies in the button handling.
Still, I'd like to have a function/module which can handle all kind of links, buttons etc. to update Tab items, not just one button
For anyone who runs into the same problem: You need to use reactive() for triggerId when calling the module
callModule(
mod_updateTabset,
"updateLink",
triggerId = reactive(input$updateButton),
dashboardId = "Tabs",
tab = "tab02",
parent = session
)
The module then needs to handle a reactive value:
mod_updateTabset <- function(
input, output, session, triggerId, dashboardId, tab, parent
) {
observeEvent(triggerId(), {
updateTabItems(parent, dashboardId, selected = tab)
})
}

Resources