如何从模块内的 modalDialog 动态生成和捕获用户输入?

Posted

技术标签:

【中文标题】如何从模块内的 modalDialog 动态生成和捕获用户输入?【英文标题】:How to dynamically generate and capture user input from modalDialog within a module? 【发布时间】:2021-12-05 15:29:57 【问题描述】:

背景:我正在创建捐赠者/样本注册申请。工作流程是这样的:

    用户选择捐赠者/样品来自哪个供应商。

    用户上传文件(使用datamods::import_file_server())。

    对该文件运行一些验证(即x 行数、y 列数等,使用datamods::validation_server()

    选择预先存在的或创建新的字段映射。

    4a) 字段映射是用户映射上传的一种机制 文件列到我们的数据库表列。

    4b) 如果新建,一个modalDialog 窗口应该会弹出,显示一个 2 列数据表,一列文件列名,一列 selectInput() 填充有我们的数据库表字段 (列)。

我的设置方式是 registration_module 处理 1-3,然后在其中,我有一个嵌套的 fieldmapping_module 作为输入:

    (已验证的)文件数据 供应商选择 和数据库列

问题:我似乎无法让动态生成的selectInput() 对 Shiny “可见”。下面是fieldmapping_module 代码。


### FieldMapping module ####

fieldmappingUI <- function(id) 
  
  tagList(
    div(
      column(8,
             selectInput(
               inputId = NS(id, "fieldmapping_selection"),
               label = "Select Field Mapping",
               choices = c("Choice 1", "Choice 2", "Choice 3") 
             ),
      ),
      column(4,
             shinyWidgets::actionBttn(
               inputId = NS(id, "create_new_fieldmapping_btn"),
               label = "Create New",
               icon = icon("file-alt"),
               size = "sm"
             )
      ),
      style = "display:inline-block",
      class = "form-group shiny-input-container")
  )



fieldmappingServer <- function(id, file_data, vendor_selection, db_cols) 
  stopifnot(is.reactive(file_data))
  stopifnot(is.reactive(vendor_selection))
  
  moduleServer(id, function(input, output, session) 
    
    ns <- session$ns
  
    #observe for creation of new FieldMappings
    observeEvent(input$create_new_fieldmapping_btn, 
      
      fieldmapping_table <- data.frame(
        "File Columns" = colnames(file_data()),
        "DB Field Mapping" = rep("", ncol(file_data()))
      )
      
      #browser()
      
      for(i in seq_len(nrow(fieldmapping_table))) 
        fieldmapping_table[i,"DB.Field.Mapping"] <- as.character(selectInput(
          inputId = glue::glue("fieldmap_select_fieldmapping_table$File.Column[i]"),
          label = NULL,
          choices = db_cols
        ))
      
      #browser()

      #display the table
      showModal(modalDialog(
        
        renderDataTable(
          DT::datatable(fieldmapping_table,
                        escape = 2,
                        selection = "none",
                        filter = 'none',
                        options = list(
                          dom = 't' 
                          ),
                        callback = JS("table.rows().every(function(i, tab, row) 
                                                       var $this = $(this.node());
                                                       $this.attr('id', this.data()[0]);
                                                       $this.addClass('shiny-input-slider-input');
                                                         );
                                                       Shiny.unbindAll(table.table().node());
                                                       Shiny.bindAll(table.table().node());")
          )

            
          ),
        title = "New Field Mapping",
        footer = tagList(
          actionButton(ns("submit_fieldmapping"), label = "Submit", icon = icon("paper-plane")),
          modalButton(label = "Close", icon = icon("window-close"))
          )
      ))
      
    )
   
    observeEvent(input$submit_fieldmapping, 
      browser()
    )
    

  )
  

异常行为:例如,假设我上传了一个包含 3 列的文件:Subject_IDCol_ACol_B,并且它已通过验证(在 @987654340 中完成) @,未显示)。

当我点击modalDialog 的提交按钮时,应用程序由于browser() 调用而暂停,我只能访问input$fieldmap_select_[column name](例如:input$fieldmap_select_Subject_ID),但我没有'吨。我认为自定义 JS 回调可以实现这一点,因为它似乎已经工作了here(以及我在谷歌搜索时遇到的其他代码/应用程序)。

browser() 暂停时,如果我在控制台中输入input 以查看输入列表,我确实看到了我创建的动态生成的输入(前三个),但它们都是NULL,尽管在 modalDialog 窗口中“设置”了它们。

我不太精通 javascript/Shiny 交互,但如果我能得到任何帮助,我将不胜感激!我做错了什么?

(交叉发布在RStudio community forum)

【问题讨论】:

【参考方案1】:

想通了。我需要在输入字段的动态生成中包含服务器端 ns 函数。即:

for(i in seq_len(nrow(fieldmapping_table))) 
        fieldmapping_table[i,"DB.Field.Mapping"] <- as.character(selectInput(
          inputId = ns(glue::glue("fieldmap_select_fieldmapping_table$File.Column[i]")),
          label = NULL,
          choices = db_cols
        ))
      

注意ns() 周围的inputId 调用。

【讨论】:

以上是关于如何从模块内的 modalDialog 动态生成和捕获用户输入?的主要内容,如果未能解决你的问题,请参考以下文章

如何在dojo Modaldialog中单击关闭按钮来捕获事件?

在 Elixir 或 Erlang 中,如何在运行时动态创建和加载模块?

如何在Elixir或Erlang中在运行时动态创建和加载模块?

我想在闪亮中创建一个可拖动的 modalDialog

如何从包内的两个不同文件夹中导入一个模块,该模块导入另一个模块

从 Android 动态功能模块将资产加载到 WebView