Shiny中eventReactive渲染R Markdown报告无数据填充问题
我正在开发一款Shiny应用,支持用户选择项目名称、月份、年份等属性,下载对应项目状态的R Markdown格式报告。
目前基础代码已编写完成,程序运行无报错,但渲染生成的报告仅显示空白框架,无法填充datasetInput()返回的数据集内容。为验证datasetInput()的有效性,我将下载逻辑替换为content = function(file){write.csv(datasetInput(), file)}直接导出CSV文件,可正常获取经过筛选的数据集,证明datasetInput()本身功能正常,现寻求能让R Markdown报告正常渲染、正确填充数据的解决方案。
现有Shiny应用代码
ui <- fluidPage( theme = bslib::bs_theme(bootswatch = "flatly"), h2("Assessment, Accountability, and Evaluation"), h4("Evaluation Project Updates"), br(), sidebarLayout( sidebarPanel( pickerInput(inputId = "project", label = "Choose project (one or more):", choices = c(levels(eval$project_name)), options = list(`actions-box` = TRUE), multiple = TRUE), pickerInput(inputId = "month", label = "Month:", choices = c(levels(eval$month)), options = list(`actions-box` = TRUE), multiple = TRUE), pickerInput(inputId = "year", label = "Year:", choices = c(levels(eval$year)), options = list(`actions-box` = TRUE), multiple = TRUE), actionButton("view", "View Selection"), downloadButton("report", "Generate report")), mainPanel( tableOutput("table") ) ) ) server <- function(input, output, session) { datasetInput <- eventReactive(input$view,{ datasetInput <- filter(eval, project_name %in% input$project, month %in% input$month, year %in% input$year) return(datasetInput) }) output$table <- renderTable({ datasetInput() %>% select(project_name, summary, month, year) }) output$report <- downloadHandler( filename = function() { paste("report-", Sys.Date(), ".html", sep = "") }, content = function(file) { tempReport <- "update.Rmd" tempReport <- file.path(tempdir(), "update.Rmd") file.copy("update.Rmd", tempReport, overwrite = TRUE) params <- list(report = datasetInput()) rmarkdown::render(tempReport, output_file = file, params = params, envir = new.env(parent = globalenv()) ) } ) } shinyApp(ui = ui, server = server)
update.Rmd模板文件代码
--- title: " " header-includes: fontsize: 11pt output: html_document: df_print: paged pdf_document: default word_document: default sansfont: Roboto font: Roboto geometry: left=1in,right=1in,top=0.35in,bottom=0.6in urlcolor: blue params: report: NA --- ```{r rmarkdown-setup, include=FALSE} knitr::opts_chunk$set(echo = FALSE) knitr::opts_chunk$set(message = FALSE) knitr::opts_chunk$set(warning = FALSE) knitr::opts_chunk$set(dev = "cairo_pdf") options(knitr.kable.NA = "") options(kableExtra.latex.load_packages = FALSE)
Project Updates
r params$project_name
r params$month r params$year
This Project Status Report summarizes information on key activities, risks, and issues for the r params$project_name project.
Project Status: r params$project_status
Project Summary: r params$summary
Project Key Area 1: r params$key_area_1
Summary of Key Area: r params$key_area_1_summary
Project Key Area 2: r params$key_area_2
Summary of Key Area: r params$key_area_2_summary
Project Key Area 3: r params$key_area_3
Summary of Key Area: r params$key_area_3_summary
Project Risks & Issuesr params$risks
Additional Notesr params$additions
## 测试用eval数据集结构 ```r dput(eval) structure(list(project_name = structure(9L, .Label = c("Equity, Disproportionality, and Design", "Coronado: iReady", "Coronado: World Language", "Oceanside: MaSTers", "California Literacy State Development Grant: Region 9", "California Literacy State Development Grant: Region 4", "National Student Clearinghouse", "iCAN: Improving Chronic Absenteeism Network", "ASPIRE: Middle School On-Track Network", "SD9UP: High School On-Track Network", "SDCOE: Equity Department", "SDCOE: California Administrative Services Credential" ), class = c("ordered", "factor")), month = structure(6L, .Label = c("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"), class = c("ordered", "factor")), day = structure(15, label = c(Q2_2 = "Entry Date: - Day")), year = structure(2L, .Label = c("2021", "2022", "2023", "2024", "2025"), class = c("ordered", "factor")), project_folder = structure(NA_character_, label = c(Q4 = "Project Folder")), project_website = structure("https://www.sdcoe.net/educators/evaluation\n", label = c(Q5 = "Project Website")), project_status = structure(1L, .Label = c("On-track", "At-risk", "Delayed"), class = c("ordered", "factor")), summary = structure("This ASPIRE Network support middle schools to keep students on track for important high school milestones including college and career readiness. The project is nearing the completion of the planning phase and is set to launch in August/September. Evaluation Services supports the network by collecting data on GPA, course-taking, and other variables. We provide customized dashboards and reports so participants can monitor their progress toward Network goals and to make decisions about changes they wish to implement in their schools. The dashboard is currently in a planning and basic design phase with the goal being a fully deployed dashboard by August 25th.", label = c(Q7 = "Summary")), key_area_1 = structure("Develop Network metrics", label = c(Q8 = "Project Key Area:")), key_area_1_summary = structure("We have started the process of developing key metrics for the dashboard. We are using GPA, passing rates in ELA and Math along with key drivers metrics including growth mindset, school culture, and relationships--all of which are captured using a survey. We have most of the survey items drafted and have developed a data collection guide for the other data elements. We continue to refine the exact criteria for what is on and off track in terms of the network using these data elements.", label = c(Q9 = "Key Area Overview:")), key_area_2 = structure("Gather, clean, and validate data", label = c(Q10 = "Project Key Area (2):")), key_area_2_summary = structure("We have designed a guide for the types of data elements we will collect. However, the Network currently has one participant. To date, we have not collected data, but have modeled the dashboard design using data from non-participating school systems. We expect to start receiving data by 07/01/2022.", label = c(Q11 = "Key Area (2) Overview:")), key_area_3 = structure("Deploy dashboards", label = c(Q12 = "Project Key Area (3):")), key_area_3_summary = structure("We have designed the dashboard's landing page and are in the process of revising it based on feedback we received. We will continue to add pages. The goal for now is to have 3 dashboard pages: a landing page, an on-track page, and a metrics page. The landing page will house all the basic information for the Network including the Aim. The on-track page will include the criteria for who's on and off-track. It will allow participants to drill down and see actual on and off-track students. The metrics page will include various data elements and allow participants to explore the data using graphs and charts. We expect to have the dashboard finalized (in draft form) by mid July with a deployment data of 08/25/2022.", label = c(Q13 = "Key Area (3) Overview:")), risks = structure("The ASPIRE Network continues to recruit participants, but to date, the network has one school committed. Ideally, we need previous year’s data so that we can see the trends in our metrics prior to starting the work. We need recruitment efforts to identify all network participants by mid-July to stay on track. One recommendation is to devote a team member to recruitment and give this person a timeline and a recruitment goal (e.g., 4 schools).\nWe need to operationalize the network aim and determine a goal for the upcoming year. To address this challenge, we need to identify network participants and collect data from the schools.\nThe survey items (network drivers) examining school culture, growth mindset, and relationships need to be operationalized and finalized. Ideally, the survey should include 10-12 items on a 3-to-5-point scale. One recommendation is to devote 3-4 hours of work time for a small group to develop the survey items fully.\n", label = c(Q14 = "Project Risks & Issues")), additions = structure("The Network's running notes and meeting agendas are available at:\nhttps://docs.google.com/document/d/1waLQOknlFnBILEivbMnKAdzibVhMoiojUn4bkBpfR70/edit\n", label = c(Q15 = "Additional notes or highlights"))), row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame"), column_map = structure(list( qname = c("StartDate", "EndDate", "Status", "IPAddress", "Progress", "Duration (in seconds)", "Finished", "RecordedDate", "ResponseId", "RecipientLastName", "RecipientFirstName", "RecipientEmail", "ExternalReference", "LocationLatitude", "LocationLongitude", "DistributionChannel", "UserLanguage", "Q1", "Q2_1", "Q2_2", "Q2_3", "Q3", "Q4", "Q5", "Q6", "Q7", "Q8", "Q9", "Q10", "Q11", "Q12", "Q13", "Q14", "Q15"), description = c("Start Date", "End Date", "Response Type", "IP Address", "Progress", "Duration (in seconds)", "Finished", "Recorded Date", "Response ID", "Recipient Last Name", "Recipient First Name", "Recipient Email", "External Data Reference", "Location Latitude", "Location Longitude", "Distribution Channel", "User Language", "Project Name:", "Entry Date: - Month", "Entry Date: - Day", "Entry Date: - Year", "Prepared by:", "Project Folder", "Project Website", "Project Status", "Summary", "Project Key Area:", "Key Area Overview:", "Project Key Area (2):", "Key Area (2) Overview:", "Project Key Area (3):", "Key Area (3) Overview:", "Project Risks & Issues", "Additional notes or highlights" ), main = c("Start Date", "End Date", "Response Type", "IP Address", "Progress", "Duration (in seconds)", "Finished", "Recorded Date", "Response ID", "Recipient Last Name", "Recipient First Name", "Recipient Email", "External Data Reference", "Location Latitude", "Location Longitude", "Distribution Channel", "User Language", "Project Name:", "Entry Date:", "Entry Date:", "Entry Date:", "Prepared by:", "Project Folder", "Project Website", "Project Status", "Summary", "Project Key Area:", "Key Area Overview:", "Project Key Area (2):", "Key Area (2) Overview:", "Project Key Area (3):", "Key Area (3) Overview:", "Project Risks & Issues", "Additional notes or highlights" ), sub = c("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Month", "Day", "Year", "", "", "", "", "", "", "", "", "", "", "", "", ""), ImportId = c("startDate", "endDate", "status", "ipAddress", "progress", "duration", "finished", "recordedDate", "_recordId", "recipientLastName", "recipientFirstName", "recipientEmail", "externalDataReference", "locationLatitude", "locationLongitude", "distributionChannel", "userLanguage", "QID1", "QID5_1", "QID5_2", "QID5_3", "QID6", "QID21_TEXT", "QID20_TEXT", "QID7", "QID10_TEXT", "QID12_TEXT", "QID13_TEXT", "QID14_TEXT", "QID15_TEXT", "QID16_TEXT", "QID17_TEXT", "QID9_TEXT", "QID18_TEXT"), timeZone = c("Z", "Z", NA, NA, NA, NA, NA, "Z", NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), choiceId = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA)), row.names = c(NA, -34L ), class = c("tbl_df", "tbl", "data.frame")))
- 参数传递与调用逻辑不匹配:Shiny端仅将筛选后的完整数据集作为
report参数传入Rmd,参数结构是数据框格式,但Rmd模板中直接调用params$project_name、params$month等独立字段,没有从传入的数据框中取值,所有字段调用均指向不存在的参数,因此无内容填充。 - Rmd行内代码存在语法错误:模板中链接部分的行内代码错误嵌套反引号,例如
rparams$project_folder`` 属于非法R语法,渲染时代码块无法正常执行。 - 缺少数据状态校验:
datasetInput()只有点击「View Selection」按钮后才会生成,若用户未点击直接生成报告,会传入空值导致渲染异常。 - YAML头配置错误:
header-includes字段后未填写任何内容,会触发渲染警告。
1. 调整Shiny服务端逻辑
增加空值校验,修正参数命名,单项目场景直接传入单行数据:
server <- function(input, output, session) { datasetInput <- eventReactive(input$view,{ req(input$project, input$month, input$year) filter(eval, project_name %in% input$project, month %in% input$month, year %in% input$year) }) output$table <- renderTable({ req(datasetInput()) datasetInput() %>% select(project_name, summary, month, year) }) output$report <- downloadHandler( filename = function() { paste("report-", Sys.Date(), ".html", sep = "") }, content = function(file) { req(datasetInput()) tempReport <- file.path(tempdir(), "update.Rmd") file.copy("update.Rmd", tempReport, overwrite = TRUE) # 单项目取第一行,多项目场景可在此处增加循环渲染、打包ZIP逻辑 params <- list(report_data = datasetInput()[1,]) rmarkdown::render(tempReport, output_file = file, params = params, envir = new.env(parent = globalenv()) ) } ) }
2. 修正Rmd模板
修复语法错误,所有字段从传入的report_data参数中取值:
--- title: " " fontsize: 11pt output: html_document: df_print: paged pdf_document: default word_document: default sansfont: Roboto font: Roboto geometry: left=1in,right=1in,

