Shiny修改输入时出现Can't extract columns that don't exist报错如何解决
问题原因
- 核心为响应式表达式的执行时序问题:重新选择列后点击按钮时,
input$select_var/input$select_var_2的更新、df_sel/df_sel_global的重计算、joined_dfs的拼接、matched_val的计算、DT的渲染几乎同时触发,旧的响应式结果还在引用已经被替换的列名,就会触发「列不存在」的报错;第二次点击时所有依赖更新完成,即可正常渲染。 - 次要问题:
dplyr的动态列选择没有用all_of()包裹字符串类型的列名,容易触发变量解析错误;formatStyle写死列索引3,健壮性差。
修复方案
对代码的核心逻辑做以下修改即可解决问题:
- 所有动态列选择/提取操作,用
all_of()包裹列名字符串,明确告知dplyr输入为列名字符串而非环境变量 - 给
joined_dfs补充必填参数校验,确保两个选中的列都存在再执行拼接 formatStyle用动态列名代替固定索引,避免后续列顺序调整后高亮逻辑失效- DT渲染前补充
joined_dfs()和matched_val()的校验,确保所有数据就绪后再执行渲染
修改后的核心代码如下:
server <- function(input, output) { # File handler ---- mydata <- reactive({ req(input$xlsxfile) inFile <- input$xlsxfile req(file.exists(input$xlsxfile$datapath)) openxlsx::read.xlsx(xlsxFile = inFile$datapath, sheet = 1 , detectDates = TRUE, sep.names = "_") }) # Dynamically generate UI input when data is uploaded, only show numeric columns ---- output$checkbox <- renderUI({ selectInput(inputId = "select_var", label = "Select variables", choices = c("", names(mydata() %>% dplyr::select_if(is.numeric))), selected = NULL, multiple = FALSE) }) # Select columns to print ---- df_sel <- reactive({ req(input$select_var) mydata() %>% dplyr::select(all_of(input$select_var), date) }) # Same as above but for global.R variable ---- output$checkbox_2 <- renderUI({ if (is.null(mydata())) return(NULL) selectInput(inputId = "select_var_2", label = "Select variables", choices = c("", names(local_iris %>% dplyr::select_if(is.numeric))), selected = NULL, multiple = FALSE) }) df_sel_global <- reactive({ req(input$select_var_2) local_iris %>% dplyr::select(all_of(input$select_var_2), Date) }) output$joinnow <- renderUI({ if (is.null(input$xlsxfile)) return() actionButton("action", "Press after selecting variables") }) # Join the dataframes together based on a key ---- joined_dfs <- eventReactive(input$action, { req(input$select_var, input$select_var_2) dplyr::inner_join(df_sel(), df_sel_global(), by= c("date" = "Date")) %>% dplyr::select(date, all_of(input$select_var), all_of(input$select_var_2)) }) # Render data frame ---- matched_val <- reactive({ req(input$action, input$select_var, input$select_var_2, joined_dfs()) ifelse( joined_dfs() %>% dplyr::pull(all_of(input$select_var)) != joined_dfs() %>% dplyr::pull(all_of(input$select_var_2)), yes = joined_dfs() %>% dplyr::pull(all_of(input$select_var_2)), no = -979025189201 ) }) output$contents <- DT::renderDT(server = FALSE, { req(input$action, joined_dfs(), matched_val()) df_render <- if(input$disp == "head") {head(joined_dfs())} else {joined_dfs()} DT::datatable( df_render, filter = 'top', extensions = c('Buttons'), options = list( scrollY = 600, scrollX = TRUE, pageLength = 20, dom = '<"float-left"l><"float-right"f>rt<"row"><"col-sm-4"B><"col-sm-4"i><"col-sm-4"p>>', lengthMenu= list(c(20, 40, 60, -1), c('20', '40', '60','All')), scrollCollapse= TRUE, lengthChange = TRUE, rownames = TRUE ) ) %>% formatStyle( columns = input$select_var_2, backgroundColor = styleEqual(levels = matched_val(), values = rep("yellow", length(matched_val()))) ) }) }
内容的提问来源于stack exchange,提问作者WannabeSmith
相关产品推荐
相关产品推荐

