I want to pass a dataset (iris
in the below reprex) between modules. Once passed, I want to click a button to download the dataset. I'm not able to download the dataset. It's part of a larger app, so I want to keep things as consistent as possible in the reprex. For instance, I want to keep the use of bs4Dash, but also I want to keep the file structure the same. This applies also to the use of boxDropdown()
and boxDropdownItem()
. As per their documentation here if I pass an id
argument, it will behave as an actionButton()
, therefore I don't use an explicit actionButton()
for the downloadHandler()
. In the reprex below, I added print statements to track and verify the process. I had tried returning a value from the module server (for instance seen here) but that didn't work. So I created this reprex to help debug this.
app/
├── global.R
├── server.R
├── ui.R
└── modules/
app/modules
├── first_module.R
└── second_module.R
To test the reprex you run the global.R
. I am trying to solve the issue of the data not being downloaded.
# Load necessary libraries
require(shiny)
require(bs4Dash)
# Source the modules
source(file = "modules/second_module.R", local = TRUE)
source(file = "modules/first_module.R", local = TRUE)
# Define the server for the Shiny app
## This isn't necessarily needed with the use of moduleServer()
## Included here in case the file is needed in the codebase
server <- function(input, output, session) {
# Call the second module's server with the iris dataset
secondModuleServer(id = "dataDownload", dataset = iris)
# Call the first module's server
firstModuleServer(id = "firstModule")
}
# Define the UI for the Shiny app
ui <- bs4DashPage(
header = bs4DashNavbar(),
sidebar = bs4DashSidebar(
sidebarMenu(
menuItem("First Module", tabName = "firstModule", icon = icon("home"))
)
),
controlbar = bs4DashControlbar(),
footer = bs4DashFooter(),
title = "Minimal Viable Shiny App",
body = bs4DashBody(
tabItems(
tabItem(
tabName = "firstModule",
firstModuleUI(id = "firstModule", tabName = "firstModuleTab")
)
)
)
)
#' A Shiny Module: Pass downloadable dataset to another module
#' @title Modularized Downloading
#' @description This module will pass iris to the other module where it should download
# Source the second module
source("modules/second_module.R", local = TRUE)
# Define the first module's UI
firstModuleUI <- function(id, tabName) {
ns <- NS(id)
tabItem(tabName = tabName,
tabPanel("First Module",
box(title = "Reprex: Modularized Download",
dropdownMenu = secondModuleUI(id = ns("dataDownload"))
)
)
)
}
# Define the first module's server
firstModuleServer <- function(id) {
moduleServer(id, function(input, output, session) {
# Pass the iris dataset to the second module's server
secondModuleServer(id = "dataDownload", dataset = iris)
})
}
# A Shiny Module - To Download/Export Data In Different File Types
#' @title Download/Export User Updated Data
#' @description Users can download/export data as CSV
# Define the second module's UI
secondModuleUI <- function(id) {
ns <- NS(id)
boxDropdown(
boxDropdownItem("CSV", id = ns("csvdownload"), icon = icon("file-csv")),
icon = icon("download")
)
}
secondModuleServer <- function(id, dataset) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
print(head(dataset)) # check if the data being received
# Trigger the downloadHandler when the download_button is clicked
observeEvent(input$csvdownload, {
print("debug: button works")
output$csvdownload <- downloadHandler(
filename = function() {
paste("iris.csv", sep = "")
},
content = function(file) {
write.csv(dataset, file, row.names = FALSE)
print("debug: download processed")
}
)
})
})
}
Thanks for your help.
Perhaps you are looking for this (no change in the remaining code).
# Define the second module's UI
secondModuleUI <- function(id) {
ns <- NS(id)
boxDropdown(
boxDropdownItem("", uiOutput(ns("csvdownload")) ),
icon = icon("download")
)
}
secondModuleServer <- function(id, dataset) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
print(head(dataset)) # check if the data being received
# Trigger the downloadHandler when the download_button is clicked
observeEvent(input$csvdownload, {
print("debug: button works")
})
output$csvdownload <- renderUI({
downloadBttn(ns("saveCSV"),
HTML("CSV"),
style = "fill",
color = "default",
size = "md",
block = TRUE,
no_outline = TRUE
)
})
output$saveCSV <- downloadHandler(
filename = function() {
paste("iris.csv", sep = "")
},
content = function(file) {
write.csv(dataset, file, row.names = FALSE)
print("debug: download processed")
}
)
})
}
# Define the UI for the Shiny app
ui <- bs4DashPage(
header = bs4DashNavbar(),
sidebar = bs4DashSidebar(
sidebarMenu(
menuItem("First Module", tabName = "firstModule", icon = icon("home"))
)
),
controlbar = bs4DashControlbar(),
footer = bs4DashFooter(),
title = "Minimal Viable Shiny App",
body = bs4DashBody(
tabItems(
tabItem(
tabName = "firstModule",
firstModuleUI(id = "firstModule", tabName = "firstModuleTab")
)
)
)
)
# Define the server for the Shiny app
## This isn't necessarily needed with the use of moduleServer()
## Included here in case the file is needed in the codebase
server <- function(input, output, session) {
# Call the second module's server with the iris dataset
#secondModuleServer(id = "dataDownload", dataset = iris)
# Call the first module's server
firstModuleServer(id = "firstModule")
}