Navigate to particular sidebar menu item in ShinyDashboard? - r

(cross post from shiny google groups, https://groups.google.com/forum/#!topic/shiny-discuss/CvoABQQoZeE)
How can one navigate to a particular sidebar menu item in ShinyDashboard?
sidebarMenu(
menuItem("Menu Item 1")
menuItem("Menu Item 2")
)
i.e. how can I put a button on the "Menu Item 1" page that will link to "Menu Item 2"?
To navigate between tabs I am using the updateTabsetPanel function:
observeEvent(input$go,{
updateTabsetPanel(session, "tabset1", selected = "Step 2")
})
I believe I should be able to use a similar function to navigate to a sidebar menu, but I am not sure what that is.
Any pointers greatly appreciated
Thanks
Iain

Is this what you are looking for? note that the example is taken from Change the selected tab on the client
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Simple tabs"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Menu Item 1", tabName = "one", icon = icon("dashboard")),
menuItem("Menu Item 1", tabName = "two", icon = icon("th"))
)
),
dashboardBody(
tabItems(
tabItem(tabName = "one",h2("Dashboard tab content"),actionButton('switchtab', 'Switch tab')),
tabItem(tabName = "two",h2("Widgets tab content"))
)
)
)
server <- function(input, output, session) {
observeEvent(input$switchtab, {
newtab <- switch(input$tabs, "one" = "two","two" = "one")
updateTabItems(session, "tabs", newtab)
})
}
shinyApp(ui, server)

Related

In Shiny how can I click in a menuItem and show the subItems options on the body?

This is my {shinydashboard}:
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
"User Name",
subtitle = a(href = "#", icon("circle", class = "text-success"), "Online"),
sidebarMenu(
id = "tabs",
menuItem(
"Dashboard",
tabName = "dashboard",
icon = icon("dashboard"),
menuSubItem(
"Widgets",
icon = icon("th"),
tabName = "widgets"
),
menuSubItem(
"Charts",
icon = icon("th"),
tabName = "charts"
)
))),
dashboardBody(
tabItems(
tabItem("dashboard",
tags$button(
h1('This button takes me to the Widgets Panel')),
br(),
br(),
tags$button(h1('This button takes me to the Charts Panel'))
))))
server = function(input, output) {
}
shinyApp(ui, server)
The idea is to click on 'Dashboard' Menu and then on the body I have a link or button that takes me to the 'Widgets' or 'Charts' Panels.
Once I click on those buttons/links OR click on the Sub Menus of 'Dashboard' menu these actions will take me to their Panels.
So, how can I add the buttons and links that take me to the panels of my Menu SubItems?
And how can I add the buttons and links on the body of my Shiny Dashboard?
Any help would me amazing.
Your question is the result of a common misunderstanding regarding shinydashboard's structure.
Childfull menuItems (like your "Dashboard" menuItem) don't have a corresponding tabItem.
The only usecase for a childfull menuItem is to expand on click and present its children (no visual change in the body - only in the sidebar).
Accordingly in your above code the tabName = "dashboard" parameter is ignored and the tabItem("dashboard", ...) isn't displayed.
When a menuItem is childfull it accepts the parameters
expandedName and startExpanded.
When a menuItem is childless it accepts the parameters tabName
and selected (just like menuSubItem which always is childless).
Please read the small print.
Also check my related answers here and here.
However, there are unofficial workarounds to have a childfull menuItem display a tabItem - I don't recommend to use them.
To navigate your tabs programmatically use updateTabItems:
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
header = dashboardHeader(),
sidebar = dashboardSidebar(
sidebarUserPanel(name = "User Name",
subtitle = a(href = "#", icon("circle", class = "text-success"), "Online"),
image = NULL),
sidebarMenu(
id = "tabs",
menuItem(
"Dashboard",
menuSubItem("Widgets",
icon = icon("th"),
tabName = "widgets"),
menuSubItem("Charts",
icon = icon("th"),
tabName = "charts"),
icon = icon("dashboard"),
expandedName = "dashboard",
startExpanded = TRUE
)
)
),
body = dashboardBody(
tabItems(
tabItem("widgets", actionButton("goCharts", "Go to Charts tab")),
tabItem("charts", actionButton("goWidgets", "Go to Widgets tab"))
)
)
)
server <- function(input, output, session) {
observeEvent(input$goCharts, {
updateTabItems(session, inputId = "tabs", selected = "charts")
})
observeEvent(input$goWidgets, {
updateTabItems(session, inputId = "tabs", selected = "widgets")
})
}
shinyApp(ui, server)

Different tabName in SideBar and TabItems

In my shinydashboard app, I want to specify a different tabName in the sidebarMenu from the tabName in the body. The server function should then catch the tabName on click and call updateTabItems.
The idea is, that some menu items in the sidebar should go to the same tab, and the longer tabName in the sideBar contains additional information used to fill the tab.
When both tabNames in sideBar and tabItems are the same, the code works. However, if I change the tabName in the sideBar, e.g. to one_param_val, updateTabItems stops working.
Any ideas why that is and what I can do about it?
library(shiny)
library(shinydashboard)
ui <- shinydashboardPlus::dashboardPage(
header=shinydashboard::dashboardHeader(title = "Switch tabs"),
sidebar=shinydashboard::dashboardSidebar(
shinydashboard::sidebarMenu(id = "tabs",
shinydashboard::menuItem(
"Menu Item 1", tabName = "one_param_val" # works for tabName="one"
),
shinydashboard::menuItem(
"Menu Item 2", tabName = "two"
)
)
),
body=shinydashboard::dashboardBody(
shinydashboard::tabItems(
shinydashboard::tabItem(
tabName = "one", h2("Content One")
),
shinydashboard::tabItem(
tabName = "two", h2("Content Two")
)
)
)
)
server <- function(input, output, session) {
shiny::observeEvent(input$tabs, {
if(grepl("^one", input$tabs)) {
message("switching to one")
shinydashboard::updateTabItems(
session, inputId="tabs", selected="one"
)
}
})
}
shinyApp(ui, server)
Here is a workaround using a hidden menuItem. However the proxy menuItem will no longer get marked as selected in the sidebar:
library(shiny)
library(shinyjs)
library(shinydashboard)
ui <- shinydashboardPlus::dashboardPage(
header=shinydashboard::dashboardHeader(title = "Switch tabs"),
sidebar=shinydashboard::dashboardSidebar(
shinydashboard::sidebarMenu(id = "tabs",
shinyjs::hidden(shinydashboard::menuItem(
"Menu Item 1", tabName = "one"
)),
shinydashboard::menuItem(
"Menu Item 1", tabName = "one_param_val"
),
shinydashboard::menuItem(
"Menu Item 2", tabName = "two"
)
)
),
body=shinydashboard::dashboardBody(
useShinyjs(),
shinydashboard::tabItems(
shinydashboard::tabItem(
tabName = "one", h2("Content One")
),
shinydashboard::tabItem(
tabName = "two", h2("Content Two")
)
)
)
)
server <- function(input, output, session) {
shiny::observeEvent(input$tabs, {
# browser()
if(grepl("^one", input$tabs)) {
message("switching to one")
shinydashboard::updateTabItems(
session, inputId="tabs", selected="one"
)
}
})
}
shinyApp(ui, server)

Open modal on menuItem click

I would like to greet the user of my app with a modal once they click on a specific menuItem in the sidebar of my ShinyDashboard. Here's a simple recreation of my previous attempt:
# libraries
library(shiny)
library(shinydashboard)
## UI ##
ui <- dashboardPage(
skin = "black",
dashboardHeader(),
dashboardSidebar(
sidebarMenu(id = "sidebarmenu",
menuItem("Dashboard", tabName = "dashboard"),
menuItem("Subitems", tabName = "subitems",
menuSubItem("Upload", "upload"),
menuSubItem("Browse", "browse")
),
menuItem("Widgets", tabName = "widgets")
)
),
dashboardBody(
uiOutput('tab')
)
)
## server ##
server <- function(input, output) {
output$tab <- renderUI({
paste("The selected tab is", input$sidebarmenu)
})
observeEvent(input$sidebarmenu == "widgets", {
showModal(
modalDialog(title = "You selected Widgets", "Or did you?")
)
})
}
shinyApp(ui, server)
The goal is to open the modal only when the menuItem widgets is selected. Despite the condition input$sidebarmenu == "widgets", this does not happen. Rather, the modal is displayed any time the user switches menuItems. Why is this the case and how can I do this properly?
Thank you in advance for any input.
Add this to the observeEvent
observeEvent(input$sidebarmenu, {
req(input$sidebarmenu == "widgets")
showModal(
modalDialog(title = "You selected Widgets", "Or did you?")
)
})

How can I adress the parent menuItem in a nested dashboardSidebar

In the example below, there are two menuItems (1 and 2) and a menuSubItem (2.1) thats nested within menuItem 2. Each menuItem has a corresponding tabItem with a box, where the boxtitle is the name of the menuItem. If i run this app "Menu 1" is initally selected and shows the box called "1". If i than click on "Menu 2" the menuSubItem expands, but the body does not change at all.
I don't think that this is the normal behaviour, is there a way to fix this?
And a second question. If I have selected "Menu 2.1" and click on "Menu 1" why does the nested menuItem do not collapse again?
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(disable=TRUE),
dashboardSidebar(
sidebarMenu(
menuItem(
"Menu 1",
tabName="menu1"
),
menuItem(
"Menu 2",
tabName = "menu2",
menuSubItem(
"Menu 2.1",
tabName = "menu2_1"
)
)
)
),
dashboardBody(
tabItems(
tabItem(tabName="menu1",
box("1")
),
tabItem(tabName="menu2",
box("2")
),
tabItem(tabName="menu2_1",
box("2.1")
)
)
)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
The behaviour you see is normal, see here
Childless menuItem()s/menuSubItem()s must be given a tabName argument
Childfull” menuItem()s cannot have a tabName or a selected argument
(or rather, they can, but this will be completely ignored by Shiny).
Instead, at most, one of them can take a startExpanded = TRUE, which
tells Shiny to start out with that menuItem() expanded, i.e. revealing
all its children
You can access the expanded tab with my code below, so you can create behavior based on that, but I do not think it is possible to display a page when clicking a menuItem that has children.
Hope this helps!
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(disable=TRUE),
dashboardSidebar(
sidebarMenu(
menuItem(
"Menu 1",
tabName="menu1"
),
menuItem(
"Menu 2",
expandedName = "menu2",
menuItem(
"Menu 2.1",
tabName = "menu2_1"
)
)
),
textOutput('selectedmenu')
),
dashboardBody(
tabItems(
tabItem(tabName="menu1",
box("1")
),
tabItem(tabName="menu2",
box("2")
),
tabItem(tabName="menu2_1",
box("2.1")
)
)
)
)
server <- function(input, output) {
output$selectedmenu <- renderText({
print(input$sidebarItemExpanded)
})
}
shinyApp(ui = ui, server = server)
Change first tab when menu 2 is expanded, request from comment:
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(disable=TRUE),
dashboardSidebar(
sidebarMenu(id="mysidebar",
menuItem(
"Menu 1",
tabName="menu1"
),
menuItem(
"Menu 2",
expandedName = "menu2",
menuItem(
"Menu 2.1",
tabName = "menu2_1"
)
)
)
),
dashboardBody(
tabItems(
tabItem(tabName="menu1",
box("1")
),
tabItem(tabName="menu2",
box("2")
),
tabItem(tabName="menu2_1",
box("2.1")
)
)
)
)
server <- function(input, output,session) {
observeEvent(input$sidebarItemExpanded, {
updateTabItems(session,"mysidebar","menu2_1")
})
}
shinyApp(ui = ui, server = server)

Dynamically populate tabName in R Shiny application

I am working on a complex shiny application. The sidebarMenu contains over 100 menu items, with some nested items. I am also using modules (callModule) to generate repeated content.
The problem I am running into is the initial loading time, which is 28 seconds. When I ran profvis I found the time was adding up because the site is generating all of the tables, plots, and content for all 100+ menu items. Therefore, I would like to generate the content when the user clicks on the menu item rather than generating it all when the site is loaded.
I am using renderUI to create the tabItems and renderMenu to create the menuItems in the server.R file.
In the small reproducible example provided below, I set my sidebarMenu id to "smenu". I call input$smenu in tabItem to set the tabName. This doesn't work as expected. If you run the example, you will notice that if you 1) click on "Dashboard", 2) click on "Dashboard2", the h3() content that should appear on the dashboard2 page does not appear. However, if you 3) click on "Dashboard" again, and 4) click on "Dashboard2" again, the content appears.
I have two questions.
1) What is the best way to generate only the content that the user asks for rather than generating it all when the site is initially loaded?
2) Why does this code show content after this sequence of clicks rather than when the user first clicks on Dashboard2?
Thanks in advance for any advice or pointers.
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
id = "smenu",
sidebarMenuOutput("menu")
)
),
dashboardBody(
textOutput("testmenu"),
uiOutput("bodyUI")
)
)
server <- function(input, output) {
observe({
output$testmenu <- renderText({
out <- paste("Current menu item is", input$smenu, sep = " ")
})
})
output$bodyUI <- renderUI({
tabItems(
tabItem(tabName = input$smenu,
h3(paste("Hello World! You are on the", input$smenu, "menu item.", sep = " "))
)
)
})
output$menu <- renderMenu({
sidebarMenu(
menuItem("Dashboard", tabName = "dashboard", icon = icon("dashboard"),
menuItem("Dashboard2", tabName = "dashboard2", icon = icon("dashboard")),
menuItem("Dashboard3", tabName = "dashboard3", icon = icon("dashboard"))
)
)
})
}
shinyApp(ui, server)
If you want to use tabName from menuItem you have to define one tab per tabName:
tabItems(
tabItem(tabName = "dashboard",
h3(paste("Hello World! You are on the", input$smenu, "menu item.", sep = " "))
),
tabItem(tabName = "dashboard2",
h3(paste("Hello World! You are on the", input$smenu, "menu item.", sep = " "))
),
tabItem(tabName = "dashboard3",
h3(paste("Hello World! You are on the", input$smenu, "menu item.", sep = " "))
)
)
This will display the tabs after the first menu selection (since the tabs are defined in the server)
Also you should move the menu id to the renderMenu:
output$menu <- renderMenu({
sidebarMenu(id = "smenu",
menuItem("Dashboard", tabName = "dashboard", icon = icon("dashboard"),
menuItem("Dashboard2", tabName = "dashboard2", icon = icon("dashboard")),
menuItem("Dashboard3", tabName = "dashboard3", icon = icon("dashboard"))
)
)
})
and simplify the ui:
...
dashboardSidebar(
sidebarMenuOutput("menu")
),
...

Resources