Usage of UIOutput in multiple menuItems in R shiny dashboard
You could create a dummy tabItem
which is hidden
and select that bu default. This will give the illusion that no tabItem
is selected. To hide the tabItem
option you could use hidden
function from shinyjs
package.
Following is the modified ui
code:
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
shinyjs::useShinyjs(),
id = "tabs",
menuItem("Charts", icon = icon("bar-chart-o"),
shinyjs::hidden(menuSubItem("dummy", tabName = "dummy")),
menuSubItem("Sub-item 1", tabName = "subitem1"),
menuSubItem("Sub-item 2", tabName = "subitem2"),
menuSubItem("Sub-item 3", tabName = "subitem3")
))),
dashboardBody(
tabItems(tabItem("dummy"),
tabItem("subitem1", uiOutput("brand_selector")),
tabItem("subitem2", 4),
tabItem("subitem3", 7))
))
EDIT1:
As per the comments and reference from the answers given bu Joe here you can do that as follows:
candyData <- read.table(
text = "
Brand Candy value
Nestle 100Grand Choc1
Netle Butterfinger Choc2
Nestle Crunch Choc2
Hershey's KitKat Choc4
Hershey's Reeses Choc3
Hershey's Mounds Choc2
Mars Snickers Choc5
Nestle 100Grand Choc3
Nestle Crunch Choc4
Hershey's KitKat Choc5
Hershey's Reeses Choc2
Hershey's Mounds Choc1
Mars Twix Choc3
Mars Vaid Choc2",
header = TRUE,
stringsAsFactors = FALSE)
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
shinyjs::useShinyjs(),
id = "tabs",
menuItem("Charts", icon = icon("bar-chart-o"),
shinyjs::hidden(menuSubItem("dummy", tabName = "dummy")),
menuSubItem("Sub-item 1", tabName = "subitem1"),
menuSubItem("Sub-item 2", tabName = "subitem2"),
menuSubItem("Sub-item 3", tabName = "subitem3")
))),
dashboardBody(
tabItems(tabItem("dummy"),
tabItem("subitem1", uiOutput("brand_selector")),
tabItem("subitem2", uiOutput("brand_selector1")),
tabItem("subitem3", uiOutput("brand_selector2")))
))
server <- function(input, output,session) {
observeEvent(input$Select1,{
updateSelectInput(session,'Select2',
choices=unique(candyData$Candy[candyData$Brand==input$Select1]))
})
observeEvent(input$Select2,{
updateSelectInput(session,'Select3',
choices=unique(candyData$value[candyData$Brand==input$Select1 &
candyData$Candy==input$Select2]))
})
output$brand_selector1 <- output$brand_selector2 <- output$brand_selector <- renderUI({
box(title = "Data", status = "primary", solidHeader = T, width = 12,
fluidPage(
fluidRow(
column(2,offset = 0, style='padding:1px;',
selectInput("Select1","select1",unique(candyData$Brand))),
column(2,offset = 0,
style='padding:1px;',selectInput("Select2","select2",choices = NULL)),
column(2, offset = 0,
style='padding:1px;',selectInput("Select3","select3",choices=NULL ))
)))
})}
shinyApp(ui = ui, server = server)
EDIT2:
Here is a slightly different approach without using renderUI
and using shinyModule
:
candyData <- read.table(
text = "
Brand Candy value
Nestle 100Grand Choc1
Netle Butterfinger Choc2
Nestle Crunch Choc2
Hershey's KitKat Choc4
Hershey's Reeses Choc3
Hershey's Mounds Choc2
Mars Snickers Choc5
Nestle 100Grand Choc3
Nestle Crunch Choc4
Hershey's KitKat Choc5
Hershey's Reeses Choc2
Hershey's Mounds Choc1
Mars Twix Choc3
Mars Vaid Choc2",
header = TRUE,
stringsAsFactors = FALSE)
library(shiny)
library(shinydashboard)
submenuUI <- function(id) {
ns <- NS(id)
tagList(
box(title = "Data", status = "primary", solidHeader = T, width = 12,
fluidPage(
fluidRow(
column(2,offset = 0, style='padding:1px;',
selectInput(ns("Select1"),"select1",unique(candyData$Brand))),
column(2,offset = 0,
style='padding:1px;',selectInput(ns("Select2"),"select2",choices = NULL)),
column(2, offset = 0,
style='padding:1px;',selectInput(ns("Select3"),"select3",choices=NULL ))
)))
)
}
# submenu <- function(input,output,session){}
submenuServ <- function(input, output, session){
observeEvent(input$Select1,{
updateSelectInput(session,'Select2',
choices=unique(candyData$Candy[candyData$Brand==input$Select1]))
})
observeEvent(input$Select2,{
updateSelectInput(session,'Select3',
choices=unique(candyData$value[candyData$Brand==input$Select1 &
candyData$Candy==input$Select2]))
})
}
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
shinyjs::useShinyjs(),
id = "tabs",
menuItem("Charts", icon = icon("bar-chart-o"),
shinyjs::hidden(menuSubItem("dummy", tabName = "dummy")),
menuSubItem("Sub-item 1", tabName = "subitem1"),
menuSubItem("Sub-item 2", tabName = "subitem2"),
menuSubItem("Sub-item 3", tabName = "subitem3")
))),
dashboardBody(
tabItems(tabItem("dummy"),
tabItem("subitem1", submenuUI('submenu1')),
tabItem("subitem2", submenuUI('submenu2')),
tabItem("subitem3", submenuUI('submenu3'))
)
))
server <- function(input, output,session) {
callModule(submenuServ,"submenu1")
callModule(submenuServ,"submenu2")
callModule(submenuServ,"submenu3")
}
shinyApp(ui = ui, server = server)
Hope it helps!
Shiny Dashboard: Render multiple menu items and output dynamic content to each
Answering my own question, but feel free to jump in if you have something more elegant.
I think my initial understanding of shiny dashboard is wrong, causing the app structure to be invalid.
The trick here is to add id
to the sidebarMenu
, so that page focus could be tracked and parsed later. Then each of the render function will listen on the input and render associated content.
shinyApp(
ui = dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
id = "sidebar_menu",
menuItemOutput("dynamic_menu"),
menuItem("Menu2", tabName = "menu2")
)
),
dashboardBody(
uiOutput("menu1_content"),
uiOutput("menu2_content")
),
title = "Example"
),
server = function(input, output, session) {
output$dynamic_menu <- renderMenu({
menu_list <- lapply(letters[1:5], function(x) {
menuSubItem(x, tabName = paste0("menu1-", x))
})
menuItem(
text = "Menu1",
startExpanded = TRUE,
do.call(tagList, menu_list)
)
})
output$menu1_content <- renderUI({
sidebar_menu <- tstrsplit(input$sidebar_menu, "-")
if (sidebar_menu[[1]] == "menu1") box(sidebar_menu[[2]])
})
output$menu2_content <- renderUI({
sidebar_menu <- tstrsplit(input$sidebar_menu, "-")
if (sidebar_menu[[1]] == "menu2") box("I am menu2")
})
}
)
Using similar UI script in R shiny under multiple subMenuItems
Your code can be rewritten with shiny modules
. The UI module where you want to display the two dropdowns can be written as one (Ui function) and then can be referred in the places you want.
Modified code:
library(shiny)
library(shinydashboard)
submenuUI <- function(id) {
ns <- NS(id)
# return a list of tags
tagList(
column(2,offset = 0, style='padding:1px;',
selectInput(ns("select1"),"select1",c("A1","A2","A3"), selected = "A1")),
column(2,offset = 0, style='padding:1px;',
selectInput(ns("select2"),"select2",c("A3","A4","A5"), selected = "A3"))
)
}
submenu <- function(input,output,session){}
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
id = "tabs",
menuItem("Charts", icon = icon("bar-chart-o"),
menuSubItem("Sub-item 1", tabName = "subitem1"),
menuSubItem("Sub-item 2", tabName = "subitem2"),
menuSubItem("Sub-item 3", tabName = "subitem3"),
menuSubItem("Sub-item 4", tabName = "subitem4")
))),
dashboardBody(
tabItems(tabItem("subitem1", submenuUI('submenu1')),
tabItem("subitem2", submenuUI('submenu2')),
tabItem("subitem3", submenuUI('submenu3')),
tabItem("subitem4", "Sub-item 2 tab content"))))
server <- function(input, output, session) {
callModule(submenu, "submenu")
}
shinyApp(ui, server)
Can you use uiOutput in a shiny Dashboard?
Once you correct the typo the following works for me. You should be able to use uiOuput
.
library(shinydashboard)
runApp(
list(ui = dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
menuItem(uiOutput("Symbols"))
)
),
dashboardBody()
)
, server = function(input, output) {
output$Symbols<-renderUI({
selectInput('Test', 'Test:', choices = c(1,2,3), selected = 1)
})
}
)
)
Using uiOutput in menuSubItem of shinydashboard
The issue got rectified by explicitly specifying the tab name of each menuSubItem
as follows:
# Create dashboard sidebar
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem(text="test1", tabName="test1",
menuSubItem(icon=NULL, tabName="test1", selectInput("x", "X", c("a", "b", "c"), selected="a")),
menuSubItem(icon=NULL, tabName="test1", uiOutput("y"))
)
)
)
R shinydashboard - show/hide multiple menuItems based on user input
Here is an idea for growing your sidebarMenu
dynamically using renderUI
and uiOutput
. It is also fairly straightforward to convert to a for loop if your number of tabs grows.
library(shiny)
library(shinydashboard)
library(shinyjs)
ui <- dashboardPage(
dashboardHeader(title = "SHOW/HIDE MULTIPLE MENU ITEMS"),
dashboardSidebar(
useShinyjs(),
uiOutput('sidebar'),
textInput(inputId = "accessToken", label = "Access Code", placeholder = "Show/Hide Menu Items.")
),
dashboardBody()
)
server <- function (input, output, session){
output$sidebar <- renderUI({
menu_items = list()
if(substr(input$accessToken,1,1)=='1')
menu_items[[length(menu_items)+1]] = menuItem("MENU ITEM 1", tabName = "mi1")
if(substr(input$accessToken,2,2)=='1')
menu_items[[length(menu_items)+1]] = menuItem("MENU ITEM 2", tabName = "mi2")
if(substr(input$accessToken,3,3)=='1')
menu_items[[length(menu_items)+1]] = menuItem("MENU ITEM 3", tabName = "mi3")
print(menu_items)
sidebarMenu(id = "tabs",menu_items)
})
}
shinyApp(ui, server)
Hope this helps!
Using shiny plotOutput in multiple places in R
This is the modularized code for multiple plotting: Very similar to the selectInput one.
library(shiny)
library(shinydashboard)
plotopUI <- function(id) {
ns <- NS(id)
# return a list of tags
tagList(
plotOutput(ns('plt'))
)
}
plotop <- function(input,output,session){
ns <- session$ns
output$plt <- renderPlot({
plot(iris$Sepal.Length)
})
}
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarMenu(
id = "tabs",
menuItem("Charts", icon = icon("bar-chart-o"),
menuSubItem("Sub-item 1", tabName = "subitem1"),
menuSubItem("Sub-item 2", tabName = "subitem2")
))),
dashboardBody(
tabItems(tabItem("subitem1", plotopUI('plt1')),
tabItem("subitem2", plotopUI('plt2')))))
server <- function(input, output, session) {
callModule(plotop, "plt1")
callModule(plotop, "plt2")
}
shinyApp(ui, server)
Shiny: Output will be shown in every menuItem
You aren't using tabName
correctly. First, you shouldn't reuse tab names in the sidebar
. Those will be clashing. A lot of your menuSubItem
tabs are have repeated values. That should be fixed to something like...
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Test1", tabName = "dashboard", icon = icon("dashboard")),
menuItem("Test2",tabName = "charts", icon = icon("bar-chart-o"),
menuSubItem("TTest1", tabName = "subitem1"),
menuSubItem("TTest2", tabName = "subitem2"),
menuSubItem("TTest3", tabName = "subitem3"),
menuSubItem("TTest4", tabName = "subitem4")),
menuItem("Test3", tabName = "choice", icon = icon("dashboard"),
menuSubItem("TTTest1", tabName = "subitem4"),
menuSubItem("TTTest2", tabName = "subitem5"),
menuSubItem("TTTest3", tabName = "subitem6"),
menuSubItem("TTTest4", tabName = "subitem7")),
menuItem("Test4", tabName = "Prod", icon = icon("dashboard"),
menuSubItem("TTTTest1", tabName = "subitem8"),
menuSubItem("TTTTest2", tabName = "subitem9"),
menuSubItem("TTTTest3", tabName = "subitem10"),
menuSubItem("TTTTest4", tabName = "subitem11"))
)
)
Notice now there are no repeated tabName
s. These are what you want to use in the dashBoardBody
to associate the sidebar with the body
of the app.
If you want your leaflet
map to appear in Test3/TTTest1, you need to use that tabName
specifically. In the code chunk above, tabName = "subitem4"
.
body <- dashboardBody(
tabItems(
# Map Output
tabItem(tabName = "subitem4",
fluidRow(
leafletOutput("myMap"),
tabBox(
title = "Legend",
id = "tabset1", height = "150px", width = "500px",
tabPanel("Explaining", "If this then that"),
tabPanel("Source", "Here you can find my data")
),
)
),
The connection between your sidebar
menu and what appears on the body
of those pages is the tabName
.
R shiny: multiple use in ui of same renderUI in server?
You can't (shouldn't) have two elements with the same ID in an HTML document (whether using Shiny or not). Certainly when using Shiny having multiple elements with the same ID will be problematic. I would also subjectively vote that you could substantially improve your code by using meaningful variable names.
It's also not really clear what you want to do with this input. Do you want the input box to be displayed on multiple tabs? Or the value of the textInput to be shown on multiple tabs?
If the former, there's not an obvious way to do that, in my mind, without violating the "multiple elements with the same ID" clause. The latter would be much easier (just use a renderText
and send it to a verbatimOutput
), but I don't think that's what you're asking.
So what you really want is multiple text inputs (with distinct IDs) that are synchronized. That you can do in separate observers on your server using something like this:
ui <- pageWithSidebar(
headerPanel("Hello !"),
sidebarPanel(
tabsetPanel(
tabPanel("a",
textInput(inputId = "text1", label = "text1", value = "")),
tabPanel("b",
textInput(inputId = "text2", label = "text2", value = ""))
)
),
mainPanel()
)
INITIAL_VAL <- "Initial text"
server <- function(input,output, session){
# Track the current value of the textInputs. Otherwise, we'll pick up that
# the text inputs are initially empty and will start setting the other to be
# empty too, rather than setting the initial value we wanted.
cur_val <- ""
observe({
# This observer depends on text1 and updates text2 with any changes
if (cur_val != input$text1){
# Then we assume text2 hasn't yet been updated
updateTextInput(session, "text2", NULL, input$text1)
cur_val <<- input$text1
}
})
observe({
# This observer depends on text2 and updates text1 with any changes
if (cur_val != input$text2){
# Then we assume text2 hasn't yet been updated
updateTextInput(session, "text1", NULL, input$text2)
cur_val <<- input$text2
}
})
# Define the initial state of the text boxes
updateTextInput(session, "text1", NULL, INITIAL_VAL)
updateTextInput(session, "text2", NULL, INITIAL_VAL)
}
runApp(list(ui=ui,server=server))
There's probably a cleaner way to set the initial state than the cur_val
I'm tracking. But I couldn't think of something off the top of my head, so there it is.
Related Topics
Testing a Function That Uses Enquo() for a Null Parameter
How to Modify Unexported Object in a Package
How to Detect That a Vector Is Subset of Specific Vector
R Shiny Widgetfunc() Warning Messages with Eventreactive(Warning 1) and Renderdatatable (Warning 2)
Converting Yearmon Column to Last Date of the Month in R
Installing Ggplot2 Package on Ubuntu
X^(1/3)' Behaves Differently for Negative Scalar 'X' and Vector 'X' with Negative Values
Splitting String Between Capital and Lowercase Character in R
R: Strptime() and Is.Na () Unexpected Results
Remove Unused Categorical Values Boxplot - R
Convert Factor to Date Class for Multiple Columns
Convert from K to Thousand (1000) in R
How to Remove Leading "0." in a Numeric R Variable
Merge Plm Fitted Values to Dataset
How to Rbind All the Data.Frames in Your Working Environment
Force a Regular Plot Object into a Grob for Use in Grid.Arrange