Toggle shinydashboard menuItems based on input parameters - shiny

I am building a large shinydashboard app that can take two kinds of data, monthly or interval. Some tabs should be shown when "Monthly" is selected from a dropdown and hidden when "Interval" is selected (and vice versa).
I tried assigning two classes, "OnlyMonthly" and "OnlyInterval," to the relevant menuItem()s by wrapping them in div() tags, and then using shinyJS's toggle() command to show ".OnlyMonthly" when "Monthly" is selected and to hide ".OnlyInterval," but the formatting of the menu is affected and it doesn't work.
Here's the code for a basic app:
require(shiny)
require(shinydashboard)
require(shinyjs)
ui <- dashboardPage(
header = dashboardHeader(title = 'Toggle Menu'),
sidebar = dashboardSidebar(
sidebarMenu(
menuItem('Item 1', tabName = 'item1',
menuSubItem('Item A', tabName = 'item1A'),
# just hide Item B
div(menuSubItem('Item B', tabName = 'item1B'), class = 'OnlyMonthly')
),
# hide all of Item 2, including C and D
div(class = 'OnlyInterval',
menuItem('Item 2', tabName = 'item2',
menuSubItem('Item C', tabName = 'item2C'),
menuSubItem('Item D', tabName = 'item2D')
)
)
)
),
body = dashboardBody(
useShinyjs(),
selectInput(inputId = 'monthly_vs_interval', label = 'Data type',choices = c('Monthly','Interval'))
)
)
server <- shinyServer(function(input, output, session) {
observe({
toggle(selector = ".OnlyMonthly", input$monthly_vs_interval == 'Monthly')
toggle(selector = ".OnlyInterval", input$monthly_vs_interval == 'Interval')
})
})
shinyApp(ui = ui, server = server)

After testing, I found that conditionalPanel properly shows/hides the tabs but the formatting is still affected. It seems sidebarMenu only allows menuItems as childs and the same is true for menuItem and menuSubItem. You can probably hide the menuItem via it's id (see ?menuItem) but there might be no way to show/hide menuSubItems without affecting the formatting.
require(shiny)
require(shinydashboard)
ui <- dashboardPage(
header = dashboardHeader(title = 'Toggle Menu'),
sidebar = dashboardSidebar(
sidebarMenu(
menuItem('Item 1', tabName = 'item1',
menuSubItem('Item A', tabName = 'item1A'),
# just hide Item B
conditionalPanel(menuSubItem('Item B', tabName = 'item1B'),
condition = "input.monthly_vs_interval == 'Monthly'")
),
# hide all of Item 2, including C and D
conditionalPanel(condition = "input.monthly_vs_interval == 'Interval'",
menuItem('Item 2', tabName = 'item2',
menuSubItem('Item C', tabName = 'item2C'),
menuSubItem('Item D', tabName = 'item2D')
)
)
)
),
body = dashboardBody(
selectInput(inputId = 'monthly_vs_interval', label = 'Data type',
choices = c('Monthly', 'Interval'))
)
)
server <- function(...){}
shinyApp(ui = ui, server = server)
Edit: Actually, only sidebarMenu has an id argument. Using an argument named id in menuSubItem leads to a syntax error and using show/hide via id for menuItems leads to unexpected results. I guess you can always code it the "dirty" way by using conditionalPanel outside of the sidebarMenu. Note however, that this approach is kind of WET.
require(shiny)
require(shinydashboard)
ui <- dashboardPage(
header = dashboardHeader(title = 'Toggle Menu'),
sidebar = dashboardSidebar(
conditionalPanel(
condition = "input.monthly_vs_interval == 'Monthly'",
sidebarMenu(menuItem(
'Item 1', tabName = 'item1',
menuSubItem('Item A', tabName = 'item1A'),
menuSubItem('Item B', tabName = 'item1B')
))
),
conditionalPanel(
condition = "input.monthly_vs_interval == 'Interval'",
sidebarMenu(
menuItem('Item 1', tabName = 'item1',
menuSubItem('Item A', tabName = 'item1A')
),
menuItem('Item 2', tabName = 'item2',
menuSubItem('Item C', tabName = 'item2C'),
menuSubItem('Item D', tabName = 'item2D')
)
)
)
),
body = dashboardBody(
selectInput(inputId = 'monthly_vs_interval', label = 'Data type',
choices = c('Monthly', 'Interval'))
)
)
server <- function(...){}
shinyApp(ui = ui, server = server)

Related

Shinydashboard. How to unselect menuItem?

I have this code:
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
shinyApp(
ui = dashboardPage(
header = dashboardHeader(userOutput("user")),
sidebar = dashboardSidebar(shinyjs::useShinyjs(),uiOutput("sidebarpanel")),
body = dashboardBody(uiOutput("body")),
title = "DashboardPage"
),
server = function(input, output) {
output$user <- renderUser({
dashboardUser(
name = "Divad Nojnarg",
image = "https://adminlte.io/themes/AdminLTE/dist/img/user2-160x160.jpg",
title = "shinydashboardPlus",
subtitle = "Author",
footer = p("The footer", class = "text-center"),
fluidRow(
actionButton("personalInfo","Personal Info")
)
)
})
observeEvent(input$personalInfo, {
output$body <- renderUI({h4("Personal Info Dahsboard (no menuItem)")})
})
output$sidebarpanel <- renderUI({
sidebarMenu(id="tabs",
menuItem("Dashboard 1", tabName = "dashboard1", icon = icon("dashboard"))
,menuItem("Dashboard 2", tabName = "dashboard2", icon = icon("dashboard"))
)
})
output$body <- renderUI({
tabItems(
tabItem(tabName ="dashboard1",
fluidRow(box(width = 12, h4("Dashboard 1 (menuItem)"))))
,tabItem(tabName ="dashboard2",
fluidRow(box(width = 12, h4("Dashboard 2 (menuItem)"))))
)
})
}
)
I would like to do two things:
First: When I click on "Personal Info" button, then, prevent the menuItem to be shadowed (I assume I need to remove the class "selected" or "active" or something like that)
Second: I want to fix this: After pressing "Personal Info" button, the menuItems do not work:
As already shown in my earlier answer here we can use a hidden menuItem to modify the body content independent from the visibly selected menuItem.
Furthermore, I'd recommend to stop using renderUI in this scenario. In general it is slower to re-render a UI element instead of updating an existing element (here we can switch to the hidden menuItem via updateTabItems - however, this applies also to e.g. using updateSelectInput instead of renderUI({selectInput(...)})). In this context you should also question whether you really need the to create the dashboardUser on the server side.
If you really need a server side generated dashboardSidebar you still should not use renderUI - There are the renderMenu() / sidebarMenuOutput() functions available for this. Please see the related docs here.
library(shiny)
library(shinyjs)
library(shinydashboard)
library(shinydashboardPlus)
shinyApp(
ui = dashboardPage(
header = dashboardHeader(userOutput("user")),
sidebar = dashboardSidebar(shinyjs::useShinyjs(),
sidebarMenu(id="tabs",
menuItem("Tab 1", tabName = "tab1", icon = icon("dashboard")),
menuItem("Tab 2", tabName = "tab2", icon = icon("dashboard")),
hidden(menuItem("Personal Tab", tabName = "personal_tab", icon = icon("dashboard")))
)),
body = dashboardBody(useShinyjs(),
tabItems(
tabItem(tabName ="tab1",
fluidRow(box(width = 12, h4("Tab 1 (menuItem)")))),
tabItem(tabName ="tab2",
fluidRow(box(width = 12, h4("Tab 2 (menuItem)")))),
tabItem(tabName ="personal_tab",
fluidRow(box(width = 12, h4("Personal Info Dahsboard (no menuItem)"))))
)
),
title = "DashboardPage"
),
server = function(input, output, session) {
output$user <- renderUser({
dashboardUser(
name = "Divad Nojnarg",
image = "https://adminlte.io/themes/AdminLTE/dist/img/user2-160x160.jpg",
title = "shinydashboardPlus",
subtitle = "Author",
footer = p("The footer", class = "text-center"),
fluidRow(
actionButton("personalInfo","Personal Info")
)
)
})
observeEvent(input$personalInfo, {
shinydashboard::updateTabItems(session, inputId = "tabs", selected = "personal_tab")
})
}
)

Is there a way to automatically upload the excel file in R

Is there a way to automatically upload the excel file.
Right now, the user has to manually upload the excel file(file.xlsx) that is kept under project folder.
Now the expected output is the moment the user clicks on "Automatically Upload the exceil file", the file should get uploaded.
Is there a way to achieve this? Let me know if this makes sense
library(tidyverse)
library(shiny)
library(shinydashboard)
library(DT)
ui <-
dashboardPage(
skin = "green",
dashboardHeader(
title = "Test",
titleWidth = 280
),
dashboardSidebar(
width = 280,
sidebarMenu(
menuItem(text = "File(s) Upload", tabName = "Files", icon = icon("file-upload")),
menuItem(text = "Output", tabName = "Out1", icon = icon("file-upload"))
)
),
dashboardBody(
tabItems(
tabItem(
tabName = "Files",
fluidRow(
column(
width = 4,
inputPanel(
fileInput(inputId = "File1", label = "File", multiple = TRUE, accept = c(".xlsx")),
selectInput(inputId = "Sheet1", label = "Select sheet", choices = NULL, selected = NULL),
actionButton("sub", "Automatically Upload the exceil file")
)
)
)
),
tabItem(
tabName = "Out1",
fluidRow(column(width = 10, strong("Data")), align = "center"),
br(),
fluidRow(dataTableOutput("Data1"))
)
)
)
)
server <- function(input, output){
# Populate the drop down menu with the names of the different Excel Sheets, but
# only after a new file is supplied
observe({
sheet_names <- readxl::excel_sheets(input$File1$datapath)
shiny::updateSelectInput(
inputId = "Sheet1",
choices = sheet_names,
selected = sheet_names[[1]]
)
}) %>%
bindEvent(input$File1)
# When the drop down meny is populated, read the selected sheet from the Excel
# file
thedata <- reactive({
req(input$Sheet1)
readxl::read_xlsx(input$File1$datapath, sheet = input$Sheet1)
})
output$Data1 <-
renderDataTable(
thedata()
, extensions = "Buttons"
, options = list(
dom = "Bfrtip"
, buttons = c("copy", "csv", "excel", "pdf", "print")
)
)
# observe({
# print(reactiveValuesToList(input, all.names = FALSE))
# })
}
runApp(
list(ui = ui, server = server)
, launch.browser = TRUE
)

Tabs of the menuItem, in Shinydashboard, not working when put items inside

Lets take the example of the reference: https://rstudio.github.io/shinydashboard/structure.html#sidebar-menu-items-and-tabs. When put more items in the menuItem(), your associate tab don't works anymore. I tried in this simple modification in example below and just shown the widgets' tab:
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Dashboard",
tabName = "dashboard",
icon = icon("dashboard"),
selected = TRUE,
startExpanded = TRUE,
numericInput("num1",
"Put the First Number",
value = 1,
min = 0),
numericInput("num2",
"Put the Second Number",
value = 1,
min = 0)
),
menuItem("Widgets",
icon = icon("th"),
tabName = "widgets")
)
)
body <- dashboardBody(
tabItems(
tabItem(tabName = "dashboard",
h2("Dashboard tab content"),
fluidRow(
valueBoxOutput("box1", width = 6),
valueBoxOutput("box2", width = 6)
)
),
tabItem(tabName = "widgets",
h2("Widgets tab content")
)
)
)
# Put them together into a dashboardPage
ui <- dashboardPage(
skin = "green",
dashboardHeader(title = "Example"),
sidebar,
body
)
server <- function(input, output){
output$box1 <- renderValueBox({
valueBox(input$num1,
"First Number",
color = "aqua",
icon = icon("chart-line"))
})
output$box2 <- renderValueBox({
valueBox(input$num2,
"Second Number",
color = "aqua",
icon = icon("chart-line"))
})
}
shinyApp(ui, server)
That is because childfull menuItem behaves differently as noted here. Therefore, you need to define a menuItem or a menSubItem within that dashboard page so that your desired content can be displayed.
Try this
sidebarMenu(id = "tabs",
menuItem("Dashboard",
tabName = "dashboard",
icon = icon("tachometer-alt"),
selected = TRUE,
startExpanded = TRUE,
#icon = icon(fontawesome:::fa_tbl[[1]][505]),
menuItem("Sub-item 1", tabName = "subitem1"),
### menuSubItem("Sub-item 1", tabName = "subitem1"), ## it can be menuSubItem instead of menuItem
numericInput("num1",
"Put the First Number",
value = 1,
min = 0),
numericInput("num2",
"Put the Second Number",
value = 2,
min = 0)
),
menuItem("Widgets",
icon = icon("th"),
tabName = "widgets")
)
)
body <- shinydashboard::dashboardBody(
tabItems(
tabItem(tabName = "subitem1",
h2("Sub item1 tab content in Dashboard"),
fluidRow(
valueBoxOutput("box1", width = 6),
valueBoxOutput("box2", width = 6)
)
),
tabItem(tabName = "widgets",
h2("Widgets tab content")
)
)
)
# Put them together into a dashboardPage
ui <- shinydashboard::dashboardPage(
skin = "green",
shinydashboard::dashboardHeader(title = "Example"),
sidebar,
body
)
server <- function(input, output, session){
output$box1 <- renderValueBox({
valueBox(input$num1,
"First Number",
color = "aqua",
icon = icon("chart-line"))
})
output$box2 <- renderValueBox({
valueBox(input$num2,
"Second Number",
color = "aqua",
icon = icon("chart-line"))
})
observe({print(input$tabs)})
}
shinyApp(ui, server)

Change color theme DT table

How can we change color theme of DT table in shiny app? By default, it uses dark and light-grey color for alternate rows. I am using formatStyle(target = 'row', backgroundColor = c('yellow', 'red'). But it does not work as it works on columns only
library(shinydashboard)
header <- dashboardHeader(title = 'title')
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem('dashboard', tabName = 'dashboard', icon = icon('dashboard'))
)
)
body <- dashboardBody(
box(
title = 'box', width = NULL, status = 'primary',
DT::dataTableOutput('table2')
)
)
ui<-dashboardPage(header, sidebar, body)
server = function(input, output) {
output$table2 = DT::renderDataTable(
iris, options = list(lengthChange = FALSE)
)
}
shinyApp(ui, server)
This should do, note that i left the header color white:
library(shinydashboard)
library(shiny)
library(DT)
header <- dashboardHeader(title = 'title')
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem('dashboard', tabName = 'dashboard', icon = icon('dashboard'))
)
)
body <- dashboardBody(
tags$style(HTML('table.dataTable tr:nth-child(even) {background-color: pink !important;}')),
tags$style(HTML('table.dataTable tr:nth-child(odd) {background-color: yellow !important;}')),
tags$style(HTML('table.dataTable th {background-color: white !important;}')),
box(
title = 'box', width = NULL, status = 'primary',
DT::dataTableOutput('table2')
)
)
ui<-dashboardPage(header, sidebar, body)
server = function(input, output) {
output$table2 = DT::renderDataTable(
iris, options = list(lengthChange = FALSE)
)
}
shinyApp(ui, server)

Shinyjs: How to detect if a user clicked on a tab?

I want to track users journey in a Shiny app and for that I need to detect if a user clicked on a tab. For that I’m using shinyjs library and a custom js tracking function. But currently I can’t come up how to use input$tabs instead of id in onclick() function. When I use tabName of a tab as id the function doesn’t react on a click.
library(shiny)
library(shinydashboard)
library(shinyjs)
ui = dashboardPage(
dashboardHeader(title = "Shiny"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Section_1", tabName = "section_1", icon = icon("align-justify"),
startExpanded = TRUE, selected = TRUE,
menuSubItem("Subsection 1", tabName = "report_1", selected = TRUE),
menuSubItem("Subsection 2", tabName = "report_2")),
menuItem("Section_2", tabName = "section_2", icon = icon("align-justify"))
)
),
dashboardBody(
useShinyjs(),
tabItems(
tabItem("report_1", h1(id = "a", "a")),
tabItem("report_2", h1(id = "b", "b")),
tabItem("section_2", h1(id = "c", "c")))
)
)
server <- function(input, output, session) {
onclick("report_1", alert("tab = report_1"))
onclick("report_2", alert("tab = report_2"))
onclick("section_2", alert("tab = section_2"))
onclick("a", alert("tab = report_1"))
onclick("b", alert("tab = report_2"))
onclick("c", alert("tab = section_2"))
}
shinyApp(ui=ui, server=server)
Thanks to that answer I have found the solution to my question. There is no need to use onclick() function at all.
library(shiny)
library(shinydashboard)
library(shinyjs)
ui = dashboardPage(
dashboardHeader(title = "Shiny"),
dashboardSidebar(
sidebarMenu(id = "tabs",
menuItem("Section_1", tabName = "section_1", icon = icon("align-justify"),
startExpanded = TRUE, selected = TRUE,
menuSubItem("Subsection 1", tabName = "report_1", selected = TRUE),
menuSubItem("Subsection 2", tabName = "report_2")),
menuItem("Section_2", tabName = "section_2", icon = icon("align-justify"))
)
),
dashboardBody(
useShinyjs(),
tabItems(
tabItem("report_1", h1(id = "a", "a")),
tabItem("report_2", h1(id = "b", "b")),
tabItem("section_2", h1(id = "c", "c")))
)
)
server <- function(input, output, session) {
observe({
if(input$tabs == "report_1") {
alert("tab = report_1")
} else if(input$tabs == "report_2"){
alert("tab = report_2")
} else {
alert("tab = section_2")
}
})
}
shinyApp(ui=ui, server=server)