How to dynamically generate and capture user input from modalDialog within a module?

Background: I am creating a Donor/Sample registration application. The workflow is as such:

  1. Users select which Vendor the Donors/Samples are from.
  2. Users upload a file (using datamods::import_file_server()).
  3. Run some validation against that file (ie. x number of rows, y number of columns, etc., using datamods::validation_server())
  4. Select pre-existing or Create a new Field Mapping.
    4a) a Field Mapping is a mechanism for users to map the uploaded file columns to our database table columns.
    4b) If Create New, a modalDialog window should pop up, showing a 2 column datatable, one column for File column names, one column of selectInput()'s that are populated with our database table fields (columns).

I have this set up in such a way that a registration_module handles 1-3, and then within that, I have a nested fieldmapping_module which takes as input:

  1. the (validated) file data
  2. the vendor selection
  3. and the database columns

Problem: I cannot seem to make the dynamically generated selectInput()'s "visible" to Shiny. Below is the fieldmapping_module code.


### 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()
    })
    

  })
  
}

Excepted behavior: For example, say I have uploaded a file with 3 columns: Subject_ID, Col_A, and Col_B, and it has passed validation (done in the registration_module, not shown).

When I hit the submit button of the modalDialog, and the app is paused due to the browser() call, I am excepting to have access to input$fieldmap_select_[column name](ex: input$fieldmap_select_Subject_ID), but I don't. I thought the custom JS callback would achieve this, as it seems to have worked here (and other code/apps I've come across while Googling).

I am not too well versed in Javascript, and how Shiny/JS interact, but would appreciate any help I could get with this! What am I doing wrong?

Small update: on the browser() pause, if I enter input into the console to see the list of inputs, I indeed do see the dynamically generated inputs I created (the first three), but they are all NULL, despite having "set" them in the modalDialog window.

I've figured it out. I was not including the server-side ns() call on the IDs of the dynamically created selectInputs()'s. The fix would be:

  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
    ))
  }

notice the ns() call around the glue function when setting the inputId.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.