繁体   English   中英

shiny 模块的无功输入

[英]Reactive input to shiny module

https://community.rstudio.com/t/reactive-input-to-module/143679交叉发布,如果这不好,请随时告诉我。 我是在这里发帖的新手。

我正在使用一个模块来处理文件上传。 它允许用户上传 CSV 或 RDS,或使用在应用程序的前一阶段生成的 dataframe。 对模块的调用将之前的 dataframe 的名称作为参数,以了解如果用户选择此选项会返回什么。

我的问题是,以前的 dataframe 似乎没有反应更新。 对于其中一个步骤(我们将其称为步骤 3),用户可以 select 是否要使用步骤 1 中的 dataframe 或步骤 2 中的 dataframe 继续。我尝试通过创建服务器端 ZA666CFDE63911BDCB45 来编写此代码, for_use_prev() ,如果选中复选框,则存储步骤 1 中的 DF,如果未选中复选框,则存储步骤 2 中的 DF。 然后模块调用for_use_prev()

但是,当for_use_prev()更改时,模块调用不会更新。 它只取 dataframe 的原始值,即使for_use_prev()改变也不复位。 再次单击上传按钮也不会强制它采用for_use_prev()的新值。

为什么模块调用没有反应性地更改为反应性 dataframe? 我尝试了各种调用方式:不带括号:

loadFileServer("calc_input", prev_file=for_use_prev)

带括号:

loadFileServer("calc_input", prev_file=for_use_prev())

包裹在反应式中,带和不带括号:

loadFileServer("calc_input", prev_file=reactive(for_use_prev()))
loadFileServer("calc_input", prev_file=reactive(for_use_prev))

他们都没有改变模块 output,尽管for_use_prev()本身肯定在改变。 此外,当按照此处的建议调用不带括号的for_use_prev时,模块返回 for_use_prev 后面的for_use_prev而不是 dataframe。

将整个模块调用包装在reactive()也不起作用。

有谁知道如何让模块调用对更改的输入做出反应?

下面是一个最小的例子。 要重现此问题,您可以在第 1 步和第 2 步中上传任意两个随机 CSV 文件。在第 3 步中, for_use_prev()的当前值显示在“当前 dataframe for_use_prev() 正在使用”下。 当您单击操作按钮“使用上一步中的文件”时,文件上传模块应 output 与for_use_prev()相同的 dataframe 将显示在for_use_prev()旁边。 它对for_use_prev()的第一个值执行此操作,但如果您取消选中操作按钮上方的复选框,您应该能够观察到for_use_prev()相应地发生变化,但该值不会从它的初始值改变,即使是for_use_prev()变化。

编辑:这是使用反应式(不带括号)而不是其值(带括号)时的样子。 该模块返回 function 而不是 dataframe。 我不愿意更改模块本身,因为它在我的应用程序中多次使用,这是唯一一个必须让用户在两个不同的先前 DF 之间进行选择的实例。

如果需要更多细节或解释,请告诉我!

代表:

library(shiny)
library(shinydashboard)

# Define the module
# Module UI function
loadFileUI <- function(id) {
  # `NS(id)` returns a namespace function, which was save as `ns` and will
  # invoke later.
  ns <- NS(id)
  
  tagList(
    actionButton(ns("file_from_prev"),"Use file from previous step"),
    h5("Or upload a saved file:"),
    fileInput(ns("file_rds"), "RDS file",accept=".rds"),
    fileInput(ns("file_csv"),"CSV File",accept=".csv"),
    actionButton(ns("file_load_rds"),"Load RDS"),
    actionButton(ns("file_load_csv"),"Load CSV"),
    actionButton(ns("file_clear"),"Remove file upload")
  )
}

# Module server function
loadFileServer <- function(id, prev_file) {
  moduleServer(
    id,
    ## Below is the module function
    function(input, output, session) {
      # initiate reactive values object to store what type of upload you want, or to clear your upload
      upload_file <- reactiveValues(state=NULL)
      observeEvent(input$file_from_prev,{ # take file from previous step
        upload_file$state <- "prev"
      })
      observeEvent(input$file_load_rds,{ # load file from rds
        upload_file$state <- "rds"
      })
      observeEvent(input$file_load_csv,{ # load file from csv
        upload_file$state <- "csv"
      })
      observeEvent(input$file_clear,{ # clear file
        upload_file$state <- "clear"
      })
      
      # actually upload the file (source depends on setting of upload_file$state as set above)
      file_full <- reactive(
        if(upload_file$state=="prev"){
          prev_file
        } else if(upload_file$state=="rds" & !is.null(input$file_rds)){
          readRDS(input$file_rds$datapath)
        }  else if(upload_file$state=="csv" & !is.null(input$file_csv)){
          read.csv(input$file_csv$datapath)
        } else if(upload_file$state=="clear"){
          NULL
        }
      )
      
      # Return the reactive that yields the data frame
      return(
        list(df=(file_full),
             status=reactive(upload_file$state))
      )
    }
  )    
}

# Set up the app
ui <- dashboardPage(
  dashboardHeader(
    title = "Reactive module input"
  ),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Step 1",tabName = "upload1"),
      menuItem("Step 2",tabName = "upload2"),
      menuItem("Step 3",tabName = "upload3")
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(
        tabName = "upload1",
        fileInput("file_up1",label = "Upload file",accept = ".csv"),
        tableOutput("input1_preview")
      ),
      tabItem(
        tabName = "upload2",
        fileInput("file_up2",label = "Upload file",accept = ".csv"),
        tableOutput("input2_preview")
      ),
      tabItem(
        tabName = "upload3",
        fluidRow(
          column(width = 6,
                 box(width = NULL,
                     checkboxInput("which_prev_input","If checked, use input 1 as previous, otherwise input 2",value=T),
                     loadFileUI("step3_input")
                     )
                 )
        ),
        fluidRow(
          column(width = 6,
                 box(width = NULL,
                     title = "Current dataframe for_use_prev() is using",
                     tableOutput("prev_df_preview")
                 )
          ),
          column(width = 6,
                 box(width = NULL,
                     title = "Dataframe being uploaded by the module",
                     tableOutput("step3_preview")
                 )
          )
        )
      )
    )
  )
)

server <- function(input, output) {
  # First file upload
  upload1 <- reactive({
    read.csv(input$file_up1$datapath)
  })
  
  output$input1_preview <- renderTable(upload1())
  
  # Second file upload
  upload2 <- reactive({
    read.csv(input$file_up2$datapath)
  })
  
  output$input2_preview <- renderTable(upload2())
  
  # Choose whether to use the first or second file
  for_use_prev <- reactive({
    if(input$which_prev_input){
      upload1()
    } else{
      upload2()
    }
  })
  
  # Call file upload module to give the possibility to upload a CSV, RDS, or use a previously uploaded file
  upload_step3_raw <- loadFileServer("step3_input", prev_file=for_use_prev()) # the call to for_use_prev doesn't update
  upload_step3_df <- reactive(upload_step3_raw$df())
  
  # Preview the DF chosen to be the previous dataframe (for_use_prev)
  output$prev_df_preview <- renderTable(head(for_use_prev()))
  
  # Preview the uploaded dataframe
  output$step3_preview <- renderTable(head(upload_step3_df()))
}

shinyApp(ui, server)

会话信息:

R version 4.2.1 (2022-06-23 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)

Matrix products: default

locale:
[1] LC_COLLATE=English_Germany.utf8  LC_CTYPE=English_Germany.utf8    LC_MONETARY=English_Germany.utf8
[4] LC_NUMERIC=C                     LC_TIME=English_Germany.utf8    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] shinydashboard_0.7.2 shiny_1.7.1         

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.8        jquerylib_0.1.4   bslib_0.3.1       later_1.3.0       pillar_1.7.0      compiler_4.2.1   
 [7] plyr_1.8.6        bitops_1.0-7      tools_4.2.1       digest_0.6.29     jsonlite_1.7.3    lifecycle_1.0.1  
[13] tibble_3.1.6      gtable_0.3.0      pkgconfig_2.0.3   rlang_1.0.1       cli_3.1.1         DBI_1.1.2        
[19] fastmap_1.1.0     dplyr_1.0.8       httr_1.4.2        xml2_1.3.3        sass_0.4.0        generics_0.1.2   
[25] vctrs_0.3.8       htmlwidgets_1.5.4 grid_4.2.1        tidyselect_1.1.2  fontawesome_0.2.2 reshape_0.8.8    
[31] glue_1.6.2        data.table_1.14.2 R6_2.5.1          fansi_1.0.2       purrr_0.3.4       ggplot2_3.3.5    
[37] magrittr_2.0.3    promises_1.2.0.1  scales_1.1.1      ellipsis_0.3.2    htmltools_0.5.2   assertthat_0.2.1 
[43] rvest_1.0.2       xtable_1.8-4      mime_0.12         colorspace_2.0-2  httpuv_1.6.5      utf8_1.2.2       
[49] munsell_0.5.0     RCurl_1.98-1.6    cachem_1.0.6      crayon_1.5.0  

我不完全清楚您要做什么,并且您的 reprex 包含许多小错误(最明显的是上传服务器 function 的返回值的错误定义以及反应性 function ( myReactive )及其当前值( myReactive() ),但这是我对你想要的最好的猜测。

在步骤 3 选项卡上:

  • 左侧框(“当前 dataframe for_use_prev() 正在使用”)根据是否选中“如果选中...”复选框进行更新
  • 右手边的盒子最初是空的
  • 单击“使用上一步中的文件”按钮时,右侧框显示与左侧框相同的数据,并响应选中和取消选中“如果选中...”复选框而更新
  • 在“CSV 文件” fileInput中加载第三个 scv 文件后,单击“加载 CSV”按钮后,右侧框会显示与右侧框不同的数据。
  • 选中“删除文件上传”按钮后,右侧复选框为空。

我相信我所做的只是实施我在原始评论中指出的必要更改。

library(shiny)
library(shinydashboard)

# Define the module
# Module UI function
loadFileUI <- function(id) {
  # `NS(id)` returns a namespace function, which was save as `ns` and will
  # invoke later.
  ns <- NS(id)
  
  tagList(
    actionButton(ns("file_from_prev"),"Use file from previous step"),
    h5("Or upload a saved file:"),
    fileInput(ns("file_rds"), "RDS file",accept=".rds"),
    fileInput(ns("file_csv"),"CSV File",accept=".csv"),
    actionButton(ns("file_load_rds"),"Load RDS"),
    actionButton(ns("file_load_csv"),"Load CSV"),
    actionButton(ns("file_clear"),"Remove file upload")
  )
}

# Module server function
loadFileServer <- function(id, prev_file) {
  moduleServer(
    id,
    ## Below is the module function
    function(input, output, session) {
      # initiate reactive values object to store what type of upload you want, or to clear your upload
      upload_file <- reactiveValues(state=NULL)
      observeEvent(input$file_from_prev,{ # take file from previous step
        upload_file$state <- "prev"
      })
      observeEvent(input$file_load_rds,{ # load file from rds
        upload_file$state <- "rds"
      })
      observeEvent(input$file_load_csv,{ # load file from csv
        upload_file$state <- "csv"
      })
      observeEvent(input$file_clear,{ # clear file
        upload_file$state <- "clear"
      })
      
      # actually upload the file (source depends on setting of upload_file$state as set above)
      file_full <- reactive(
        if(upload_file$state=="prev"){
          prev_file()
        } else if(upload_file$state=="rds" & !is.null(input$file_rds)){
          readRDS(input$file_rds$datapath)
        }  else if(upload_file$state=="csv" & !is.null(input$file_csv)){
          read.csv(input$file_csv$datapath)
        } else if(upload_file$state=="clear"){
          NULL
        }
      )
      
      rv <- reactive({
        req(input$file_from_prev)
        list(df=file_full(), status=upload_file$state)
      })
      
      # Return the reactive that yields the data frame
      return(rv)
    }
  )    
}

# Set up the app
ui <- dashboardPage(
  dashboardHeader(
    title = "Reactive module input"
  ),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Step 1",tabName = "upload1"),
      menuItem("Step 2",tabName = "upload2"),
      menuItem("Step 3",tabName = "upload3")
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(
        tabName = "upload1",
        fileInput("file_up1",label = "Upload file",accept = ".csv"),
        tableOutput("input1_preview")
      ),
      tabItem(
        tabName = "upload2",
        fileInput("file_up2",label = "Upload file",accept = ".csv"),
        tableOutput("input2_preview")
      ),
      tabItem(
        tabName = "upload3",
        fluidRow(
          column(width = 6,
                 box(width = NULL,
                     checkboxInput("which_prev_input","If checked, use input 1 as previous, otherwise input 2",value=T),
                     loadFileUI("step3_input")
                 )
          )
        ),
        fluidRow(
          column(width = 6,
                 box(width = NULL,
                     title = "Current dataframe for_use_prev() is using",
                     tableOutput("prev_df_preview")
                 )
          ),
          column(width = 6,
                 box(width = NULL,
                     title = "Dataframe being uploaded by the module",
                     tableOutput("step3_preview")
                 )
          )
        )
      )
    )
  )
)

server <- function(input, output) {
  # First file upload
  upload1 <- reactive({
    req (input$file_up1)
    
    read.csv(input$file_up1$datapath)
  })
  
  output$input1_preview <- renderTable(upload1())
  
  # Second file upload
  upload2 <- reactive({
    req (input$file_up2)
    
    read.csv(input$file_up2$datapath)
  })
  
  output$input2_preview <- renderTable(upload2())
  
  # Choose whether to use the first or second file
  for_use_prev <- reactive({
    if(input$which_prev_input){
      upload1()
    } else{
      upload2()
    }
  })
  
  # Call file upload module to give the possibility to upload a CSV, RDS, or use a previously uploaded file
  upload_step3_raw <- loadFileServer("step3_input", prev_file=for_use_prev) # the call to for_use_prev doesn't update
  upload_step3_df <- reactive({ upload_step3_raw()$df })
  # Preview the DF chosen to be the previous dataframe (for_use_prev)
  output$prev_df_preview <- renderTable(head(for_use_prev()))
  
  # Preview the uploaded dataframe
  output$step3_preview <- renderTable({
    req(upload_step3_df())
    
    head(upload_step3_df())
  })
}

shinyApp(ui, server)

您很可能不得不更改模块,因为您定义其返回值的方式,我相信,根本上是不正确的,因为原始定义不允许应用程序的其他部分做出反应性响应。

避免将来出现这种情况的一种方法是在开始在应用程序中的许多不同位置使用它之前彻底测试模块的行为方式是否正确。

欢迎来到 SO。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM