I am trying to work on a functioniality for our shiny app where in the user can download a powerpoint with all the tables and charts. I did see a standalone app where I know how to use it if all the tables and plots are in the server component. Since our code base is increasing and we are trying to use modules to break the app I am unable to identify where should I have the downloadhandler. If I have it in the server component how can I pass my tables and plots from the modules to this function in server. Below is the code of a standalone download to powerpoint code.
library(shiny)
library(officer)
library(flextable)
library(dplyr)
my_table <- data.frame(
Name = letters[1:4],
Age = seq(20, 26, 2),
Occupation = LETTERS[15:18],
Income = c(50000, 20000, 30000, 45000)
)
ui <- fluidRow(
column(
width = 12,
align = "center",
tableOutput("data"),
br(),
downloadButton("download_powerpoint", "Download Data to PowerPoint")
)
)
server <- function(input, output) {
output$data <- renderTable({
my_table
})
output$download_powerpoint <- downloadHandler(
filename = function() {
"employee_data.pptx"
},
content = function(file) {
flextable_prep <- flextable(my_table) %>%
colformat_num(col_keys = c("Age", "Income"), digits = 0) %>%
width(width = 1.25) %>%
height_all(height = 0.35) %>%
theme_zebra() %>%
align(align = "center", part = "all")
example_pp <- read_pptx() %>%
add_slide(layout = "Title Slide", master = "Office Theme") %>%
ph_with_text(
type = "ctrTitle",
str = "Employee Data"
) %>%
ph_with(
location = ph_location_type(type = "subTitle"),
value = "Company 2019 Report"
) %>%
add_slide(layout = "Title and Content", master = "Office Theme") %>%
ph_with_text(
type = "title",
str = "2019 Data"
) %>%
ph_with_flextable_at(
value = flextable_prep,
left = 2.5,
top = 2
)
print(example_pp, target = file)
}
)
}
shinyApp(ui, server)
There are several ways to pass data from a module to another.
You can for example return a reactive from one module, and use it in another.
See (I removed the powerpoint generation here to focus on the implementation of the reactivity) :
library(shiny)
library(officer)
library(flextable)
library(dplyr)
showui <- function(id){
ns <- NS(id)
tagList(
selectInput(ns("table"), "table", choices = c("iris", "mtcars")),
tableOutput(ns("data"))
)
}
show <- function(input, output, session){
ns <- session$ns
my_table <- reactive({
get(input$table)
})
output$data <- renderTable({
head(my_table())
})
my_table
}
dlui <- function(id){
ns <- NS(id)
tagList(
downloadButton(
ns("download_powerpoint"),
"Download Data"
)
)
}
dl <- function(input, output, session, my_table){
ns <- session$ns
output$download_powerpoint <- downloadHandler(
filename = function() {
"employee_data.csv"
},
content = function(file) {
write.csv(my_table(), file)
}
)
}
ui <- fluidRow(
column(
width = 12,
align = "center",
showui("showui"),
br(),
dlui("dlui")
)
)
server <- function(input, output) {
my_table <- callModule(show, "showui")
callModule(dl, "dlui", my_table)
}
shinyApp(ui, server)
Related
I am trying to download my file after filtering my data. when i run the below code the result is the only empty file is downloaded (not the filtered data).
Please note that this is multiple shiny app not a single one.
the first part is the UI and second part is the server.
please see the code below:
library(shiny)
#library(DT)
library(dplyr)
library(ggplot2)
#UI
fluidPage(
titlePanel("IN_PATIENT"),
# Create a new Row in the UI for selectInputs
fluidRow(
column(2,selectInput("HOSPITAL_NAME",
"Hospital Name:",
c("All",
unique(as.character(IN_PATIENT$HOSPITAL_NAME))))
),
column(2,selectInput("FINAL_GENDER",
"Gender:",
c("All",
unique(as.character(IN_PATIENT$FINAL_GENDER))))
),
column(2,selectInput("FINAL_NATIONALITY_STATUS",
"Nationality Status:",
c("All",
unique(as.character(IN_PATIENT$FINAL_NATIONALITY_STATUS))))
)
),
# Button
downloadButton("downloadData", "Download"),
DT::dataTableOutput("table")
)
#Server
function(input, output,session) {
# Filter data based on selections
output$table <- DT::renderDataTable(DT::datatable({
df <- IN_PATIENT
if (input$HOSPITAL_NAME != "All") {
df <- df[df$HOSPITAL_NAME == input$HOSPITAL_NAME,]
}
if (input$FINAL_GENDER != "All") {
df <- df[df$FINAL_GENDER == input$FINAL_GENDER,]
}
if (input$FINAL_NATIONALITY_STATUS != "All") {
df <- df[df$FINAL_NATIONALITY_STATUS == input$FINAL_NATIONALITY_STATUS,]
}
df
}))
output$downloadData <- downloadHandler(
filename = function() {
paste("df-",Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(df, file, row.names = FALSE)
}
)
}
Since you don't provide sample data, I made test data with mtcars. The best solution in Shiny is to make your data as a reactive object that is passed to renderDT and downloadHandler:
library(shiny)
library(DT)
library(dplyr)
library(ggplot2)
IN_PATIENT <- mtcars %>% tibble::rownames_to_column() %>% rename(HOSPITAL_NAME = rowname,
FINAL_GENDER = vs,
FINAL_NATIONALITY_STATUS = carb)
ui <- fluidPage(
titlePanel("IN_PATIENT"),
# Create a new Row in the UI for selectInputs
fluidRow(
column(2,selectInput("HOSPITAL_NAME",
"Hospital Name:",
c("All",
unique(as.character(IN_PATIENT$HOSPITAL_NAME))))
),
column(2,selectInput("FINAL_GENDER",
"Gender:",
c("All",
unique(as.character(IN_PATIENT$FINAL_GENDER))))
),
column(2,selectInput("FINAL_NATIONALITY_STATUS",
"Nationality Status:",
c("All",
unique(as.character(IN_PATIENT$FINAL_NATIONALITY_STATUS))))
)
),
# Button
downloadButton("downloadData", "Download"),
DT::DTOutput("table")
)
#Server
server <- function(input, output,session) {
df <- reactive({
df <- IN_PATIENT
if (input$HOSPITAL_NAME != "All") {
df <- df[df$HOSPITAL_NAME == input$HOSPITAL_NAME,]
}
if (input$FINAL_GENDER != "All") {
df <- df[df$FINAL_GENDER == input$FINAL_GENDER,]
}
if (input$FINAL_NATIONALITY_STATUS != "All") {
df <- df[df$FINAL_NATIONALITY_STATUS == input$FINAL_NATIONALITY_STATUS,]
}
df
})
# Filter data based on selections
output$table <- DT::renderDT(datatable(df()))
output$downloadData <- downloadHandler(
filename = function() {
paste("df-",Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(df(), file, row.names = FALSE)
})
}
shinyApp(ui, server)
My objective is to plot some graphs based on the data uploaded by users. Users can select how it is plotted. It works but there are warnings showing in the console. I think the issue lies with how the server module looks up values in the UI module but the UI module is not yet initalised. How can I get rid of the warnings? Thanks
Sample data:
fwrite(data.table(
age.band = c("45-54","55-64","55-64","55-64","55-64","45-54","35-44","25-34"),
gender = c("MALE","FEMALE","FEMALE","FEMALE","FEMALE","FEMALE","MALE","FEMALE"),
event = c("13/04/2022","8/04/2022","20/05/2021","12/02/2022","19/02/2021","19/03/2022","16/03/2021","19/03/2021"),
cause = c('Cancer','Cancer','Cancer','Mental Illness','Cancer','Musculoskeletal','Mental Illness','Musculoskeletal'),
type= c('Type1','Type1','Type1','Type2','Type2','Type2','Type2','Type2'),
rate = rep(1,8),
status = rep("accepted",8)
),"sample.csv",row.names=F)
Here is my code:
### UI and server to load data
loadDataUI <- function(id,label = "Upload",buttonLabel = "Browse") {
ns <- NS(id)
tagList(
fileInput(ns("file"),label=label,buttonLabel=buttonLabel,accept = c('.csv'),placeholder = "No file selected")
)
}
loadData <- function(id) {
moduleServer(
id,
function(input, output, session) {
userFile <- reactive({
shiny::validate(need(input$file, message = FALSE))
input$file
})
dt <- reactive({fread(userFile()$datapath)})
return(dt)
}
)
}
### UI and server to plot data
edaUI <- function(id,cat.option) {
ns <- NS(id)
tagList(
box(
dropdown(
selectInput(inputId = ns('category'),
label = 'Category',
choices = cat.option,selected = "all"),
dateRangeInput(ns("eventdaterange"), "Loss date range",
start = Sys.Date()-10,
end = Sys.Date()+10)
),
highchartOutput(ns("hc_init"))
)
)
}
eda <- function(id,filtered.data,measure){
moduleServer(
id,
function(input, output, session) {
observeEvent(filtered.data(), {
updateDateRangeInput(session,"eventdaterange",
start = min(filtered.data()$event),
end = max(filtered.data()$event))
})
output$hc_init <- renderHighchart({
tmp <- filtered.data() %>%
filter(event >= input$eventdaterange[1],event <= input$eventdaterange[2]) %>%
mutate(event.date = as.character(as.yearqtr(event)))
if (input$category == "all") {
tmp %>% group_by(event.date) %>%
summarise(measure=sum(!!sym(measure))) %>%
hchart("line", hcaes(x = event.date, y = measure)) %>%
hc_title(text="Chart")
} else {
tmp %>%
group_by(cat = !!sym(input$category),event.date) %>%
summarise(measure=sum(!!sym(measure))) %>% ungroup() %>%
arrange(cat,event.date) %>%
hchart("line", hcaes(x = event.date, y = measure,group = cat)) %>%
hc_title(text="Chart")
}
})
}
)
}
### Main UI and server
ui <- function() {
fluidPage(useShinydashboard(),
navbarPage(
tabPanel(
title = "Data analysis",
sidebarLayout(
sidebarPanel(
width = 2,
loadDataUI("input","","Upload data"),
awesomeRadio(
inputId = "benefit",
label = strong("Select type"),
choices = c("Type1", "Type2"),
selected = NULL,inline = TRUE,checkbox = FALSE)
),
mainPanel(
width = 10,edaUI("dt1",cat.option=c("all","age.band","gender"))
)
)
)
)
)
}
server <- function(input, output, session) {
data <- loadData("input")
filtered_data <- reactive({
date.cols <- c("event")
data()[type == input$benefit][,(date.cols):= lapply(.SD, dmy),.SDcols = date.cols]
})
eda("dt1",filtered_data,measure="rate")
}
shinyApp(ui = ui, server = server)
I'm trying to use {{gt}} in conjunction with {{shinyscreenshot}}. The resulting screenshot seems to crop numeric columns:
Is this due to rendering in the gt package or caused by shinyscreenshot and how can this be avoided?
Thanks!
library(shiny)
library(gt)
library(magrittr)
library(shinyscreenshot)
gt_tbl <-
gtcars %>%
gt() %>%
cols_hide(contains("_"))
ui <- fluidPage(
gt_output(outputId = "table"),
actionButton("screenshot", "Screenshot gt"),
)
server <- function(input,
output,
session) {
output$table <-
render_gt(
expr = gt_tbl,
height = px(600),
width = px(600)
)
observeEvent(input$screenshot, {
shinyscreenshot::screenshot(id = "table")
})
}
if (interactive()) {
shinyApp(ui, server)
}
this worked: saves the gt as temp file and uses file.copy to download
library(shiny)
library(gt)
library(dplyr)
ui <- fluidPage(
downloadButton("report", "Generate Report")
)
server <- function(input, output, session) {
my_table <- reactive({
mtcars[1:5, 1:5] %>%
gt()
})
my_image <- reactive({
outfile <- tempfile(fileext = ".png")
gtsave(data = my_table(),
filename = outfile,
vwidth = 400,
vheight = 300)
outfile
})
output$report <- downloadHandler(
filename = "download.png",
content = function(file) {
file.copy(my_image(), file)
},
contentType = 'image/png'
)
}
shinyApp(ui, server)
I use Timevis package.
first of all I read an excel file with missions.
In my code the user can see all the missions on a time line, and he can edit/add/remove any missions.
after the user make a change I can see the update table below.
I want to save to my excel file every update that the user make.
this is my code:
library(shiny)
library(timevis)
library(readxl)
my_df <- read_excel("x.xlsx")
data <- data.frame(
id = my_df$id,
start = my_df$start,
end = my_df$end,
content = my_df$content
)
ui <- fluidPage(
timevisOutput("appts"),
tableOutput("table")
)
server <- function(input, output) {
output$appts <- renderTimevis(
timevis(
data,
options = list(editable = TRUE, multiselect = TRUE, align = "center")
)
)
output$table <- renderTable(
input$appts_data
)
}
shinyApp(ui, server)
You can use actionButton/ observe to call saveworkbook (package openxlsx) to save your changes. Technically you are not saving these changes, but replacing the file with an identical file containing the changes.
library(shiny)
library(openxlsx)
library(timevis)
library(readxl)
my_df <- read_excel("x.xlsx")
data <- data.frame(
id = my_df$id,
start = my_df$start,
end = my_df$end,
content = my_df$content
)
mypath = paste0(getwd(), "/x.xlsx") # Path to x.xlsx
ui <- fluidPage(
timevisOutput("appts"),
tableOutput("table"),
actionButton("save", "Save")
)
server <- function(input, output) {
output$appts <- renderTimevis(
timevis(
data,
options = list(editable = TRUE, multiselect = TRUE, align = "center")
))
observeEvent(input$save,
{
my_df<- createWorkbook()
addWorksheet(
my_df,
sheetName = "data"
)
writeData(
wb = my_df,
sheet = "data",
x = input$appts_data,
startRow = 1,
startCol = 1
)
saveWorkbook(my_df, file = mypath,
overwrite = TRUE)
})
output$table <- renderTable(
input$appts_data
)
}
shinyApp(ui, server)
I can create a data table in shiny that shows data for any individual buffalo but I can't figure out how to display all buffalo data at the same time. Any help is appreciated.
Sample Data:
cleanbuffalo <- data.frame(name = c("queen","toni","pepper"),
longitude = c(31.8,32,33),
latitude = c(-24,-25,-26))
Shiny UI:
shinyUI(navbarPage("Buffalo Migration", id ="nav",
tabPanel("Data",
fluidRow(
column(3,
selectInput("allnamesbuffalo", "Buffalo", c("All Buffalo" = "all buffalo", vars))
)
),
hr(),
DT::dataTableOutput("buffalotable")
)
)
)
Shiny Server:
shinyServer(function(input, output, session) {
observe({
allnamesbuffalo <- if (is.null(input$allnamesbuffalo)) character(0) else {
filter(cleanbuffalo, name %in% input$allnamesbuffalo) %>%
`$`('name') %>%
unique() %>%
sort()
}
})
output$buffalotable <- DT::renderDataTable({
df <- cleanbuffalo %>%
filter(
cleanbuffalo$name == input$allnamesbuffalo,
is.null(input$allnamesbuffalo) | name %in% cleanbuffalo$name
)
action <- DT::dataTableAjax(session,df)
DT::datatable(df, options = list(ajax = list(url = action)),
escape = FALSE)
})
})
Here is a working example. Note that I added stringsAsFactors=F in your data frame, otherwise you need to use levels(cleanbuffalo$name) to get the names.
library(shiny)
library(dplyr)
cleanbuffalo <- data.frame(name = c("queen","toni","pepper"),
longitude = c(31.8,32,33),
latitude = c(-24,-25,-26), stringsAsFactors = F)
ui <- shinyUI(fluidPage(
titlePanel("Example"),
sidebarLayout(
sidebarPanel(
selectInput("allnamesbuffalo", "Buffalo", c("all", cleanbuffalo$name))
),
mainPanel(
dataTableOutput("buffalotable")
)
)
))
server <- shinyServer(function(input, output, session) {
output$buffalotable <- renderDataTable({
names <- NULL
if (input$allnamesbuffalo == "all") {
names <- cleanbuffalo$name
} else {
names <- input$allnamesbuffalo
}
filter(cleanbuffalo, name %in% names)
})
})
shinyApp(ui = ui, server = server)