Download filtered tableOutput in Shiny - r

I have the following data:
> data
products id
1 P1 280386
2 P1 285184
3 P2 293154
4 P1 294245
I have built a simple shiny code. I first filter the table and then I want to download the filtered table. I write the following
library(shiny)
library(shinyWidgets)
library(tidyverse)
library(DT)
data <- read.csv("Desktop/data.csv")
products <- unique(data$products)
ui <- fluidPage(
fluidRow(
column(4,
selectInput("product", "Product", products,
multiple = TRUE),
downloadButton("download", "Download")),
column(8,
tableOutput("errorTable")
)
)
)
server <- function(input, output, session) {
output$errorTable <- renderTable({
subset(data, products == input$product)
}
)
output$download <- downloadHandler(
filename = function() {
paste("data-",Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(data, file)
}
)
}
shinyApp(ui, server)
However, this code only downloads the full table, not the filtered one. I have searched some question but none explained this case specifically. thanks in advance

Try this
library(shiny)
library(shinyWidgets)
library(tidyverse)
library(DT)
data <- read.csv("Desktop/data.csv")
products <- unique(data$products)
ui <- fluidPage(
fluidRow(
column(4,
selectInput("product", "Product", products,
multiple = TRUE),
downloadButton("download", "Download")),
column(8,
tableOutput("errorTable")
)
)
)
server <- function(input, output, session) {
#you need to create a reactive object with a NULL starting value
listofitems <- reactiveValues(data = NULL )
#observe the changes in input$product and update the reactive object
observeEvent( input$product, {
print("Hello: observeEvent for input$product is triggered")
#debug using browser()
listofitems$data <- subset(data, products == input$product)
showNotification("Products updated",
type = "message",
duration = 4,
closeButton = TRUE)
}, ignoreInit = T,ignoreNULL = TRUE)
output$errorTable <- renderTable({
listofitems$data
}
)
output$download <- downloadHandler(
filename = function() {
paste("data-",Sys.Date(), ".csv", sep = "")
},
content = function(file) {
write.csv(listofitems$data, file)
}
)
}
shinyApp(ui, server)

Related

Multiple UI Output Based on File Upload Using Shiny Modules

Dataset Viewer
Hello, I am attempting to create a shiny application that allows to user to view their uploaded datasets individually.
When there is no file uploaded a message appears asking the user to upload their file...once a csv file is uploaded, the message disappears and shows the users uploaded datasets.
What I've tried
I've tried: using conditionalPanels in app.R & upload.R, creating a separate R file exclusively for each ui condition. I believe my issue is that output$table (function that renders mainpanel ui) is not being triggered after the file uploads.
My issue
Once the user uploads a csv file(any readable csv file), the pre-existing message is not being replaced by the uploaded datasets.
upload.R
data = list()
numDatasets = 0
uploadSideUI <- function(id) {
ns <- NS(id)
tagList(
h2("Dataset Viewer"),
fileInput(ns("file"),label = "Upload File", multiple = FALSE, accept = ".csv")
)
}
uploadMainUI <- function(id) {
ns <- NS(id)
uiOutput(ns("table"))
}
uploadServer <- function(id) {
moduleServer(id, function(input,output,session){
observeEvent(eventExpr = input$file,
handlerExpr = {
df <- read.csv(file = input$file$datapath,header = FALSE)
data <<- c(data,list(df))
numDatasets <<- numDatasets + 1
})
output$table <- renderUI({
if(numDatasets ==0){
h2("please upload file")
}else{
req(input$file)
print(numDatasets)
lapply(1:numDatasets,function(i) {
dataframe = data[[i]]
tagList(
h2(paste("dataset",i)),
hr(),
datatable(dataframe,rownames = FALSE, option = list(scrollY="300px",searching=FALSE)),
br()
)
})
}
})
})
}
app.R
#app.R
library(DT)
library(shiny)
source("testModule.R")
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
uploadSideUI("uploadPage")
),mainPanel(
uploadMainUI("uploadPage")
)
)
)
server <- function(input, output, session) {
uploadServer("uploadPage")
}
shinyApp(ui = ui, server = server)
I am new to the modulization process in shiny, so if you have any other suggestions please point them out! Thanks in advance!
Try this
uploadServer <- function(id) {
moduleServer(id, function(input,output,session){
rv <- reactiveValues(numDatasets = 0)
observeEvent(eventExpr = input$file,
handlerExpr = {
df <- read.csv(file = input$file$datapath,header = FALSE)
data <<- c(data,list(df))
rv$numDatasets <<- rv$numDatasets + 1
})
output$table <- renderUI({
if(rv$numDatasets == 0){
h2("please upload file")
}else{
req(input$file)
print(rv$numDatasets)
lapply(1:rv$numDatasets,function(i) {
dataframe = data[[i]]
tagList(
h2(paste("dataset",i)),
hr(),
datatable(dataframe,rownames = FALSE, option = list(scrollY="300px",searching=FALSE)),
br()
)
})
}
})
})
}

How to create a button that will create a pdf file of a table

I currently have a table being generated and I would like the user to be able to create a pdf file when they click the download button.
I am currently getting an error where when I click the download button I get an html file that downloads the entire page of the app. I thought that using pdf(file) would work but it ignores the function.
Here is currently what I have.
library(shiny)
library(xlsx)
library(shinyWidgets)
population <- read.xlsx("population.xlsx", 1)
fieldsMandatory <- c("selectedCountry")
labelMandatory <- function(label) {
tagList(
label,
span("*", class = "mandatory_star")
)
}
appCSS <-
".mandatory_star {color: red;}"
ui <- fluidPage(
navbarPage(title = span("Spatial Tracking of COVID-19 using Mathematical Models", style = "color:#000000; font-weight:bold; font-size:15pt"),
tabPanel(title = "Model",
sidebarLayout(
sidebarPanel(
shinyjs::useShinyjs(),
shinyjs::inlineCSS(appCSS),
div(
id = "dashboard",
pickerInput(
inputId = "selectedCountry",
labelMandatory ("Country"),
choices = population$Country,
multiple = FALSE,
options = pickerOptions(
actionsBox = TRUE,
title = "Please select a country")
),
sliderInput(inputId = "agg",
label = "Aggregation Factor",
min = 0, max = 50, step = 5, value = 10),
actionButton("go","Run Simulation"),
)
),
mainPanel(
tabsetPanel(
tabPanel("Input Summary", verbatimTextOutput("summary"),
tableOutput("table"),
downloadButton(outputId = "downloadSummary", label = "Save Summary"))
)
)
)
)
)
)
server <- function(input, output, session){
observeEvent(input$resetAll, {
shinyjs::reset("dashboard")
})
values <- reactiveValues()
values$df <- data.frame(Variable = character(), Value = character())
observeEvent(input$go, {
row1 <- data.frame(Variable = "Country", Value = input$selectedCountry)
row2 <- data.frame(Variable = "Aggregation Factor", Value = input$agg)
values$df <- rbind(row1, row2)
})
output$table <- renderTable(values$df)
observe({
# check if all mandatory fields have a value
mandatoryFilled <-
vapply(fieldsMandatory,
function(x) {
!is.null(input[[x]]) && input[[x]] != ""
},
logical(1))
mandatoryFilled <- all(mandatoryFilled)
# enable/disable the submit button
shinyjs::toggleState(id = "go", condition = mandatoryFilled)
})
output$downloadSummary <- downloadHandler(
filename = function(file) {
paste('my-report.pdf', )
},
content = function(file) {
pdf(file)
}
)
}
shinyApp(ui,server)
Here's a minimal example:
library(shiny)
ui <- fluidPage(
downloadButton("savepdf", "Save pdf")
)
server <- function(input, output, session) {
output$savepdf <- downloadHandler(
filename = "test.pdf",
content = function(file) {
pdf(file)
plot(iris$Sepal.Length, iris$Sepal.Width)
dev.off()
}
)
}
shinyApp(ui, server)
Also see here.
Here is a minimal example with the package latexpdf. It will create the pdf table in the folder of the app.
library(shiny)
library(latexpdf)
dat <- head(iris, 5)
ui <- fluidPage(
br(),
actionButton("dwnld", "Create pdf"),
tableOutput("mytable")
)
server <- function(input, output, session){
output[["mytable"]] <- renderTable({
dat
})
observeEvent(input[["dwnld"]], {
as.pdf(dat)
})
}
shinyApp(ui, server)

How to download editable data table in shiny

In data table, we can use argument editable to make the table editable. I'm making a shiny app in which table is both editable and downloadable.
My question is how I can download a datatable after I edit it?
Here is my app code:
library(shiny)
library(DT)
server <- function(input, output) {
df = iris
output$data = DT::renderDataTable ({
DT::datatable(df, editable = list(
target = 'row',
disable = list(columns = c(1, 3, 4))
))
})
output$downloadData <- downloadHandler(
filename = function() {
#paste(input$dataset, ".csv", sep = "")
},
content = function(file) {
write.csv(df, file, row.names = FALSE)
}
)
}
ui <- fluidPage(
DT::dataTableOutput('data'),
downloadButton("downloadData", "Download")
)
shinyApp(ui = ui, server = server)
When you edit a cell of a datatable named "XXX", the info about the cell edit is in input$XXX_cell_edit. This info contains the indices of the edited cell and its new value. So you can do:
library(shiny)
library(DT)
dat <- iris[1:3, ]
ui <- fluidPage(
downloadButton("downloadData", "Download"),
DTOutput("table")
)
server <- function(input, output){
output[["table"]] <- renderDT({
datatable(dat, editable = "cell")
})
df <- reactiveVal(dat)
observeEvent(input[["table_cell_edit"]], {
cell <- input[["table_cell_edit"]]
newdf <- df()
newdf[cell$row, cell$col] <- cell$value
df(newdf)
})
output[["downloadData"]] <- downloadHandler(
filename = function() {
"mydata.csv"
},
content = function(file) {
write.csv(df(), file, row.names = FALSE)
}
)
}
shinyApp(ui, server)
Alternatively, as suggested by #MrGumble, you can use the embedded button of Datatables instead of a downloadHandler. This is more stylish.
library(shiny)
library(DT)
dat <- iris[1:3, ]
ui <- fluidPage(
DTOutput("table")
)
server <- function(input, output){
output[["table"]] <- renderDT({
datatable(dat, editable = "cell", extensions = "Buttons",
options = list(
dom = "Bfrtip",
buttons = list(
"csv"
)
))
})
observeEvent(input[["table_cell_edit"]], {
cellinfo <- input[["table_cell_edit"]]
dat <<- editData(dat, input[["table_cell_edit"]], "table")
})
}
shinyApp(ui, server)
You can add a download button directly to a DT datatable, which offers the user to download the current data in the table, see R Shiny: How to add download buttons in DT::renderDataTable
If you however want to use the edited data for server-side calculations, you are are on the right track, but need to save the edited table into the data.frame using replaceData. See e.g. https://yihui.shinyapps.io/DT-edit/

Use download button with dataframe inside observe event

I have a application that allows user to upload the data, then an action button to trigger some calculation. I then have some progress bar to show user how long the process would take. Once the calculation finish, I would like to add a download button so user can download the calculation result.
I am not sure how to access the datatable created inside observe event function so I can use it in the downloadhandler function?
Here is my code:
server <- function(input, output) {
options(shiny.maxRequestSize=200*1024^2)
file_name <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
return (stringi::stri_extract_first(str = inFile$name, regex = ".*(?=\\.)"))
})
output$myFileName <- renderText({ paste("Claim data selected:",file_name()) })
mydata <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
tbl <- read.csv(inFile$datapath,sep=";")
return(tbl)
})
output$my_output_data <- DT::renderDataTable({
mydata() },
options = list(
lengthChange = FALSE,
autowidth = TRUE,
columnDefs = list(list(width = '70%', targets = 1)))
)
output$summary <- renderText({
dt.size <- nrow({mydata()})
paste("There are",dt.size,"records.", sep =" ")
})
observeEvent(input$goButton1,{
output$table1 <- DT::renderDataTable({
withProgress(message = 'Calculation in progress...',
value = 0, {function1({mydata()},progress=TRUE)})
})
output$table1 <- DT::renderDataTable(function1({mydata()}))
output$downloadData <- downloadHandler(
filename = function() {
paste("DLR result-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
#fwrite("output$table 1 should be here", file)
})
})
}
The issue with the code you have given is that the fread function from the data.table library takes a data.frame or data.table argument. Here you have given it a DT javascript DataTable object. My code uses base R data.frames instead of data.table, but you should be able to adapt it accordingly.
library(shiny)
library(shinydashboard)
ui = dashboardPage(
dashboardHeader(title = "File Download"),
dashboardSidebar(),
dashboardBody(
fluidPage(
fluidRow(
box(width=12,
title = "UploadDownload",
fileInput("file1", label="File1 upload"),
downloadButton("downloadData", "Download")
)
),
fluidRow(
box(width=12,
title = "DataTable",
textOutput("myFileName"),
DT::dataTableOutput("my_output_data")
)
)
)
)
)
server = function(input, output) {
file_name = reactive({
req(input$file1)
return(gsub("\\..*$", "", input$file1$name))
})
output$myFileName = renderText({
paste("Claim data selected:",file_name())
})
mydata = reactive ({
req(input$file1)
tbl = read.csv(input$file1$datapath)
return(tbl)
})
mydata2 = reactive ({
tbl = mydata()
# a calculation that will take some time
withProgress(message="Adding another column", detail="this may take some time",
{
n = dim(tbl)[2]
tbl$newcolumn = NULL
for (i in 1:n) {
tbl$newcolumn[i] = sample.int(10,1)
incProgress(1/n)
Sys.sleep(5/n)
}
})
})
output$my_output_data = DT::renderDataTable(
mydata(),
options = list(
lengthChange=FALSE,
autowidth=TRUE,
columnDefs=list(list(width='70%', targets=1))
)
)
output$downloadData = downloadHandler(
filename = function() {
paste("DLR results-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(mydata2(), file)
}
)
}
shinyApp(ui, server)

read csv shiny module?

I'm trying to learn how to use shiny modules. I started with a simple app that is very similar to the one in the documentation. The app ask you to check a box, then you can upload a csv file, and it will show you a table with the data:
## app.R ##
library(shiny)
library(shinydashboard)
library(shinyjs)
# Header
header <- dashboardHeader()
# Sidebar
sidebar <- dashboardSidebar(
checkboxInput("agree", p("I read ",
a("the very important stuff",
href="http://stackoverflow.com/",
target="_blank")), FALSE),
fileInput(
"chosenfile",
label = h4("File input"),
accept = ".csv"
))
# Body
body <- dashboardBody(
useShinyjs(),
box(
title = "Test",
width = 12,
solidHeader = TRUE,
status = "warning",
dataTableOutput('tbl')
)
)
# ui
ui <- dashboardPage(header, sidebar, body)
# server
server <- function(input, output) {
#Load the chosen dataset
data <- reactive({
dfile <-
input$chosenfile[1, 4] # <- filename with path is the [1,4] cell in obj
if (!is.null(dfile))
readr::read_csv(dfile)
})
output$tbl <- renderDataTable(data(),
options = list(scrollX = TRUE,
pageLength = 10,
searching = FALSE))
observe({
if (input$agree == T) {
# enable the download button
shinyjs::enable("chosenfile")
}
})
observe({
if (input$agree == F) {
# enable the download button
shinyjs::disable("chosenfile")
}
})
}
#run
shinyApp(ui, server)
I want to create a module that has the check the box, upload the file part of the app.
Right now I have this:
# Module
# Module UI function
csvFileInput <- function(id, label = "CSV file") {
# Create a namespace function using the provided id
ns <- NS(id)
tagList(
checkboxInput(ns("agree"), p("I read ",
a("the very important stuff",
href="http://stackoverflow.com/",
target="_blank"))),
fileInput(ns("file"), label)
)
}
# Module server function
csvFile <- function(input, output, session) {
# The selected file, if any
userFile <- reactive({
# If no file is selected, don't do anything
validate(need(input$file, message = FALSE))
input$file
})
# The user's data, parsed into a data frame
dataframe <- reactive({
readr::read_csv(userFile()$datapath)
})
# We can run observers in here if we want to
observe({
msg <- sprintf("File %s was uploaded", userFile()$name)
cat(msg, "\n")
})
# Return the reactive that yields the data frame
return(dataframe)
}
## app.R ##
library(shiny)
library(shinydashboard)
library(shinyjs)
# Header
header <- dashboardHeader()
# Sidebar
sidebar <- dashboardSidebar(
csvFileInput("datafile", "CSV file")
)
# Body
body <- dashboardBody(
useShinyjs(),
box(
title = "Test",
width = 12,
solidHeader = TRUE,
status = "warning",
dataTableOutput("table")
)
)
# ui
ui <- dashboardPage(header, sidebar, body)
# server
server <- function(input, output) {
datafile <- callModule(csvFile, "datafile")
output$table <- renderDataTable({
datafile()
})
}
#run
shinyApp(ui, server)
I'm not sure how to implement the the enable/disable part of the module.
I tried this, but the app crashes:
# Module
# Module UI function
csvFileInput <- function(id, label = "CSV file") {
# Create a namespace function using the provided id
ns <- NS(id)
tagList(
checkboxInput(ns("agree"), p("I read ",
a("the very important stuff",
href="http://stackoverflow.com/",
target="_blank"))),
fileInput(ns("file"), label)
)
}
# Module server function
csvFile <- function(input, output, session) {
# The selected file, if any
userFile <- reactive({
# If no file is selected, don't do anything
validate(need(input$file, message = FALSE))
input$file
})
# The user's data, parsed into a data frame
dataframe <- reactive({
readr::read_csv(userFile()$datapath)
})
# We can run observers in here if we want to
observe({
msg <- sprintf("File %s was uploaded", userFile()$name)
cat(msg, "\n")
})
# Return the reactive that yields the data frame
return(dataframe)
}
diable_button <- function(input, output, session, button, agree){
observe({
if (agree == T) {
# enable the download button
shinyjs::enable(button)
}
})
observe({
if (agree == F) {
# enable the download button
shinyjs::disable(button)
}
})
}
## app.R ##
library(shiny)
library(shinydashboard)
library(shinyjs)
# Header
header <- dashboardHeader()
# Sidebar
sidebar <- dashboardSidebar(
csvFileInput("datafile", "CSV file")
)
# Body
body <- dashboardBody(
useShinyjs(),
box(
title = "Test",
width = 12,
solidHeader = TRUE,
status = "warning",
dataTableOutput("table")
)
)
# ui
ui <- dashboardPage(header, sidebar, body)
# server
server <- function(input, output) {
datafile <- callModule(csvFile, "datafile")
callModule(diable_button, "datafile",
button = input$chosenfile,
agree = input$agree)
output$table <- renderDataTable({
datafile()
})
}
#run
shinyApp(ui, server)

Resources