Related
I have made a dynamic report visually in an r shiny app using renderui. I would like to be able to download this dynamic report but not sure how to correctly create it assuming I am unable to convert a render ui into an html file.
What is the best way to write a dynamic html file that can be displayed in the ui? and then download it?
Below is a minimal reproducible project. The download button is currently just for show.
library(shiny)
library(shinydashboard)
library(dplyr)
library(stringr)
library(DBI)
library(DT)
library(shinycssloaders)
library(lubridate)
library(tidyr)
library(ggplot2)
library(plotly)
library(scales)
ui <- dashboardPage(
dashboardHeader(title = "Key Performance Indicators", titleWidth =300),
dashboardSidebar(width = 300,
sidebarMenu(
menuItem("User Guide", tabName = "userguide", icon = icon("question-circle")),
menuItem("Dashboard", tabName = "dashboard", icon = icon("chart-line"), selected = TRUE)
),
selectizeInput(inputId="goals",
label="Goal:",
choices= c("Asset Management"
),
selected= "Asset Management",
multiple = FALSE),
uiOutput("kpis")
),
dashboardBody(
tabItems(
tabItem(
tabName = "userguide",
fluidRow(column(width = 12,
tabBox(width = NULL,
tabPanel("User Guide",
h3("General"),
h5("")
)
)
)
)
),
tabItem(
tabName = "dashboard",
fluidRow(column(width = 12,
tabBox(width = NULL,
tabPanel("Plot",
plotlyOutput("plot", height = 550) %>%
withSpinner(color="#1b6d96")),
tabPanel("Report",
uiOutput("report") %>%
withSpinner(color="#1b6d96")
)
)
)
)
)
)
)
)
server <- function(input, output) {
rawTable <- reactive({
df <- data.frame(KPI =c("Money Spent"),
measure = c("Dollars"),
FY2015= c(500),
FY2016= c(100),
FY2017= c(250),
FY2018= c(600),
FY2019= c(750),
FY2020= c(900))
return(df)
})
output$kpis <- renderUI({
selectizeInput(inputId="kpi",
label="KPI:",
choices= unique(rawTable()$KPI),
selected= unique(rawTable()$KPI[1]),
multiple = FALSE)
})
KPIplot <- reactive({
req(input$kpi)
df <- rawTable() %>%
filter(KPI == input$kpi) %>%
tidyr::pivot_longer(cols = tidyr::starts_with("FY"),
names_to = "Fiscal.Year",
values_to = "Value") %>%
mutate(Values = as.numeric(gsub("[^A-Za-z0-9;._-]","",Value)))
#measure <- toupper(unique(df$`Y Axis Label`))
ggplotly(
ggplot(
data = df,
aes(x = Fiscal.Year, y= Value,
text = paste0("Fiscal Year: ", gsub("\\.","-",str_remove(Fiscal.Year, "FY")),
"<br>Value: ", Value))
) +
geom_bar(stat = "identity") +
scale_y_continuous(labels = comma, breaks = scales::pretty_breaks(n = 10)) +
theme_minimal(),
tooltip = c("text")
)
})
output$plot <- renderPlotly({KPIplot()})
output$report <- renderUI({
fluidPage(
fluidRow(
column(
8, align = "right", offset = 2,
downloadButton("report", "Generate report")
)
),
fluidRow(
column(
8, align="center", offset = 2,
h1("Key Performance Indicator"),
hr(),
h2(input$goals)
)
),
fluidRow(
column(
8, align="left", offset = 2,
h2(input$kpi),
br(),
h3("Description"),
h5("custom text"),
br(),
h3("Performance Data"),
renderPlotly({KPIplot()}),
br(),
h3("Analysis"),
h5("custom text")
)
)
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I followed the tutorial on creating a dashboard with Shiny and semantic.dashboard: https://appsilon.com/create-outstanding-dashboards-with-the-new-semantic-dashboard-package/
The example uses a custom theme named "cerulean", but it doesn't appear when running the app. Changing the theme to another one doesn't have any impact either. Does someone know the correct way to change the theme? The example code uses the theme option of dashboardPage as described in the documentation. I didn*t change anything, but the screenshots from the tutorial look different.
Thanks a lot for your help!
library(shiny)
library(semantic.dashboard)
library(ggplot2)
library(plotly)
library(DT)
ui <- dashboardPage(
dashboardHeader(color = "blue",title = "Dashboard Demo", inverted = TRUE),
dashboardSidebar(
size = "thin", color = "teal",
sidebarMenu(
menuItem(tabName = "main", "Main", icon = icon("car")),
menuItem(tabName = "extra", "Extra", icon = icon("table"))
)
),
dashboardBody(
tabItems(
selected = 1,
tabItem(
tabName = "main",
fluidRow(
box(width = 8,
title = "Graph 1",
color = "green", ribbon = TRUE, title_side = "top right",
column(width = 8,
plotOutput("boxplot1")
)
),
box(width = 8,
title = "Graph 2",
color = "red", ribbon = TRUE, title_side = "top right",
column(width = 8,
plotlyOutput("dotplot1")
)
)
)
),
tabItem(
tabName = "extra",
fluidRow(
dataTableOutput("carstable")
)
)
)
), theme = "cerulean"
)
server <- shinyServer(function(input, output, session) {
data("mtcars")
colscale <- c(semantic_palette[["red"]], semantic_palette[["green"]], semantic_palette[["blue"]])
mtcars$am <- factor(mtcars$am,levels=c(0,1),
labels=c("Automatic","Manual"))
output$boxplot1 <- renderPlot({
ggplot(mtcars, aes(x = am, y = mpg)) +
geom_boxplot(fill = semantic_palette[["green"]]) +
xlab("gearbox") + ylab("Miles per gallon")
})
output$dotplot1 <- renderPlotly({
ggplotly(ggplot(mtcars, aes(wt, mpg))
+ geom_point(aes(colour=factor(cyl), size = qsec))
+ scale_colour_manual(values = colscale)
)
})
output$carstable <- renderDataTable(mtcars)
})
shinyApp(ui, server)
I'm trying to create a dashboard using Shiny. Here is some sample data:
###Creating Data
name <- c("Sharon", "Megan", "Kevin")
x <- c(5, 7,3)
y <- c(3,6,2)
z <- c(2,3,7)
jobForm = data.frame(name, x, y, z)
What I'm trying to figure out is, for every row of names how do I create their own TABLE? I believe there is a way to create a reactive for-loop but I've been at this for a long time and have given up.
Here is the full code of what the dashboard should look like for each name. This code only shows Sharon's scores, and it should run. If there are any issues on getting the code to run completely let me know.
I am using
packages shiny, shinydashboard and tidyverse
##Dashboard Header
header <- dashboardHeader(
title = "My Project")
##Dashboard Sidebar
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Dashboard", icon = icon("dashboard"),tabName = "dashboard"),
menuItem("Job Positions", icon = icon("address-card"), tabName = "jobposition",
menuSubItem('Sales',
tabName = 'sales',
icon = icon('line-chart'))
)
)
)
##Dashboard Body
body <- dashboardBody(
tabItems(
# Dashboard Tab Content
tabItem(tabName = "dashboard",
fluidRow(
#Random Plot
box( )
)
),
# Associate Tab Content
tabItem(tabName = "sales",
fluidRow(
#Main Box for Candidate
box(
width = 8,
title = "Candidate 001",
status = "primary",
#Box for Table
box(
title = "Table",
status = "info",
tableOutput("stat1")
)
)
)
)
)
)
##User Interface Using Dashboard Function
ui <- dashboardPage(
skin = "yellow",
header,
sidebar,
body
)
##Server: Instructions
server <- function(input, output) {
temp <- data.frame(jobForm %>%
slice(1) %>%
select(x:z))
temp <- as.data.frame(t(temp))
output$stat1 <-renderTable({
temp
},
include.rownames=TRUE,
colnames(temp)<-c("Score")
)
}
##Create Shiny App Object
shinyApp(ui, server)
Thank you for any help
You better solve these kibnd of problems with an renderUI and since you never really know when shiny will evaluate an expression you are much better of using lapply then for loops.
name <- c("Sharon", "Megan", "Kevin")
x <- c(5, 7,3)
y <- c(3,6,2)
z <- c(2,3,7)
jobForm = data.frame(name, x, y, z)
##Dashboard Header
header <- dashboardHeader(
title = "My Project")
##Dashboard Sidebar
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Dashboard", icon = icon("dashboard"),tabName = "dashboard"),
menuItem("Job Positions", icon = icon("address-card"), tabName = "jobposition",
menuSubItem('Sales',
tabName = 'sales',
icon = icon('line-chart'))
)
)
)
##Dashboard Body
body <- dashboardBody(
tabItems(
# Dashboard Tab Content
tabItem(tabName = "dashboard",
fluidRow(
#Random Plot
box( )
)
),
# Associate Tab Content
tabItem(tabName = "sales",
fluidRow(
#Main Box for Candidate
uiOutput("candidates")
)
)
)
)
##User Interface Using Dashboard Function
ui <- dashboardPage(
skin = "yellow",
header,
sidebar,
body
)
##Server: Instructions
server <- function(input, output) {
temp <- data.frame(jobForm %>%
slice(1) %>%
select(x:z))
temp <- as.data.frame(t(temp))
output$stat1 <-renderTable({
temp
},
include.rownames=TRUE,
colnames(temp)<-c("Score")
)
output$candidates <- renderUI(
tagList(
lapply(1:nrow(jobForm), function(idx){
output[[paste0("stat",idx)]] <- renderTable(
jobForm[idx,-1]
)
box(
width = 8,
title = paste0("Candidate: ",jobForm$name[idx]),
status = "primary",
#Box for Table
box(
title = "Table",
status = "info",
tableOutput(paste0("stat",idx))
)
)
})
)
)
}
##Create Shiny App Object
shinyApp(ui, server)
Hope this helps!!
The code currently produces the dashboard and the side panels for the plots, it also produces one plot. (which ever is called first in the final line). If anyone has any suggestions on how to produce two separate plots on two different tabs using a shiny dashboard it would be much appreciated. Tearing my hair out!
https://lot1bct.shinyapps.io/lot1bct/
My current state, minus the "fuels" tab and second plot for reference on what i'm aiming for.
The code below has the second plot code mocked up along with the additional dashboard code in the "fuels" tab which the live version does not.
## app.R ##
library(shinydashboard)
library(shiny)
library(ggplot2)
dataset <- testData
fuelData <- fuelDataCSV
tyreData <- tyreDataCSV
ui <- dashboardPage(
skin="green",
dashboardHeader(title = "Strategy Dashboard v0.1",
dropdownMenu(type = "tasks", badgeStatus = "success",
taskItem(value = 10, color = "green",
"Documentation"
),
taskItem(value = 30, color = "aqua",
"UI"
),
taskItem(value = 15, color = "yellow",
"Data Developmentt"
)
)),
## Sidebar content
dashboardSidebar(
sidebarMenu(
menuItem("Home", tabName = "home", icon = icon("home")),
menuItem("Tyres", tabName = "tyres", icon = icon("circle-o")),
menuItem("Fuels and Lubericants", tabName = "fuel", icon = icon("flask")),
menuItem("Times", tabName = "times", icon = icon("clock-o")),
menuItem("Documentation", tabName = "documentation", icon = icon("sticky-note-o")),
menuItem("Downloads", tabName = "downloads", icon = icon("download")),
# Custom CSS to hide the default logout panel
tags$head(tags$style(HTML('.shiny-server-account { display: none; }'))),
# The dynamically-generated user panel
uiOutput("userpanel")
)
),
## Header Content
## Body content
dashboardBody(
tabItems(
# First tab content
tabItem(tabName = "home",
h2("UI Tests"), p("This published version is the first test build (version 0.1).")
),
# Second tab content
tabItem(tabName = "tyres",
h2("Tyre data sets"), fluidPage(
sidebarPanel(
sliderInput('sampleSize', 'Sample Size (Laps)', min=1, max=nrow(tyreData),
value=min(20, nrow(tyreData)), step=1, round=0),
selectInput('x', 'X', names(tyreData)),
selectInput('y', 'Y', names(tyreData), names(tyreData)[[2]]),
selectInput('color', 'Color', c('None', names(tyreData))),
checkboxInput('density', 'Density'),
checkboxInput('trend', 'Trend')
),
mainPanel(
plotOutput('plotT')
)
)
),
# Third tab content
tabItem(tabName = "fuel",
h2("Fuel and Lubricant data sets"),
fluidPage(
sidebarPanel(
sliderInput('sampleSize', 'Sample Size (Laps)', min=1, max=nrow(fuelData),
value=min(20, nrow(fuelData)), step=1, round=0),
selectInput('x', 'X', names(fuelData)),
selectInput('y', 'Y', names(fuelData), names(fuelData)[[2]]),
selectInput('color', 'Color', c('None', names(fuelData))),
checkboxInput('density', 'Density'),
checkboxInput('trend', 'Trend')
),
mainPanel(
plotOutput('plotF')
)
)
),
# Fourth tab content
tabItem(tabName = "times",
h2("Times data sets")
),
# Fifth tab content
tabItem(tabName = "documentation",
h2("Documentation")
),
# Sixth tab content
tabItem(tabName = "downloads",
h2("Downloads")
)
)
)
)
tyrePlot <- function(input, output) {
tyreData <- reactive({
tyreDataCSV[sample(nrow(tyreDataCSV), input$sampleSize),]
})
output$plotF <- renderPlot({
p <- ggplot(tyreData(), aes_string(x=input$x, y=input$y)) + geom_point()
if (input$color != 'None')
p <- p + aes_string(color=input$color)
if (input$density)
p <- p + geom_density_2d()
if (input$trend)
p <- p + geom_smooth()
print(p)
}, height=700)
}
fuelPlot <- function(input, output) {
fuelData <- reactive({
fuelDataCSV[sample(nrow(fuelDataCSV), input$sampleSize),]
})
output$plotF <- renderPlot({
p <- ggplot(fuelData(), aes_string(x=input$x, y=input$y)) + geom_point()
if (input$color != 'None')
p <- p + aes_string(color=input$color)
if (input$density)
p <- p + geom_density_2d()
if (input$trend)
p <- p + geom_smooth()
print(p)
}, height=700)
}
shinyApp(ui, tyrePlot, fuelPlot)
I am facing an issue with shiny dashboard. I am trying to create a simple dashboard with two tabItems on the left. Each tabItem have their specific set of controls and a plot. But I am probably missing something on the server side to link the input to the tab because the controls of the second tab is behaving strangely. Any help would be much appreciated. Here is my code
library(shiny)
library(shinydashboard)
library(data.table)
library(ggplot2)
data = data.table(group = rep(c(1, 3, 6), each = 10), x = rep(1:10, times = 3), value = rnorm(30))
sidebar <- dashboardSidebar(
sidebarMenu(id = 'sidebarMenu',
menuItem("tab 1", tabName = "tab1", icon = icon("dashboard")),
menuItem("tab 2", icon = icon("th"), tabName = "tab2")
)
)
body <- dashboardBody(
tabItems(
tabItem(tabName = "tab1",
fluidRow(
box(title = "Controls",
checkboxGroupInput('group', 'group:', c(1, 3, 6), selected = 6, inline = TRUE), width = 4),
box(plotOutput("plot1"), width = 8)
)
),
tabItem(tabName = "tab2",
fluidRow(
box(title = "Controls",
checkboxGroupInput('group', 'group:', c(1, 3, 6), selected = 6, inline = TRUE), width = 4),
box(plotOutput("plot2"), width = 8)
)
)
)
)
# Put them together into a dashboardPage
ui <- dashboardPage(
dashboardHeader(title = "test tabbed inputs"),
sidebar,
body,
skin = 'green'
)
server <- function(input, output) {
output$plot1 <- renderPlot({
plotData <- data[group %in% input$group]
p <- ggplot(plotData, aes(x = x, y = value, colour = factor(group))) + geom_line() + geom_point()
print(p)
})
output$plot2 <- renderPlot({
plotData <- data[group %in% input$group]
p <- ggplot(plotData, aes(x = x, y = value, colour = factor(group))) + geom_line() + geom_point()
print(p)
})
}
shinyApp(ui, server)
When I change input in the first tab it also changes in the second and then when I try to change it back often time nothing happens or it just behaves weirdly. I think I need to specify tie the input to the tabItems somehow but could not find a good example of doing that. Any help would be much appreciated.
Thanks,
Ashin
To deal with a dynamic number of tabs or other widgets, create them in server.R with renderUI. Use a list to store the tabs and the do.call function to apply the tabItems function. The same for the sidebar.
I think my code below generates your expectation.
library(shiny)
library(shinydashboard)
library(data.table)
library(ggplot2)
data = data.table(group = rep(c(1, 3, 6), each = 10), x = rep(1:10, times = 3), value = rnorm(30))
sidebar <- dashboardSidebar(
uiOutput("Sidebar")
)
body <- dashboardBody(
uiOutput("TABUI")
)
# Put them together into a dashboardPage
ui <- dashboardPage(
dashboardHeader(title = "test tabbed inputs"),
sidebar,
body,
skin = 'green'
)
server <- function(input, output) {
ntabs <- 3
tabnames <- paste0("tab", 1:ntabs) # "tab1", "tab2", ...
checkboxnames <- paste0(tabnames, 'group') # "tab1group", "tab2group", ...
plotnames <- paste0("plot", 1:ntabs) # "plot1", "plot2", ...
output$Sidebar <- renderUI({
Menus <- vector("list", ntabs)
for(i in 1:ntabs){
Menus[[i]] <- menuItem(tabnames[i], tabName = tabnames[i], icon = icon("dashboard"), selected = i==1)
}
do.call(function(...) sidebarMenu(id = 'sidebarMenu', ...), Menus)
})
output$TABUI <- renderUI({
Tabs <- vector("list", ntabs)
for(i in 1:ntabs){
Tabs[[i]] <- tabItem(tabName = tabnames[i],
fluidRow(
box(title = "Controls",
checkboxGroupInput(checkboxnames[i], 'group:', c(1, 3, 6), selected = 6, inline = TRUE),
width = 4),
box(plotOutput(paste0("plot",i)), width = 8)
)
)
}
do.call(tabItems, Tabs)
})
RV <- reactiveValues()
observe({
selection <- input[[paste0(input$sidebarMenu, 'group')]]
RV$plotData <- data[group %in% selection]
})
for(i in 1:ntabs){
output[[plotnames[i]]] <- renderPlot({
plotData <- RV$plotData
p <- ggplot(plotData, aes(x = x, y = value, colour = factor(group))) +
geom_line() + geom_point()
print(p)
})
}
}
shinyApp(ui, server)
Note that I put the "plot data" in a reactive list. Otherwise, if I did that:
output[[plotnames[i]]] <- renderPlot({
selection <- input[[paste0(input$sidebarMenu, 'group')]]
plotData <- data[group %in% selection]
...
the plot would be reactive each time you go back to a tab (try to see what I mean).