title: R Shiny: How to dynamically append arbitrary number of input widgets
body: <h2>The goal</h2>
  
  <p>I am working on a Shiny app that allows the user to upload their own data and focus on the entire data or a subset by providing data filtering widgets described by the below graph
  <a href="https://i.stack.imgur.com/1UinW.png" rel="noreferrer"><img src="https://i.stack.imgur.com/1UinW.png" alt="enter image description here"></a>
  The select input "<strong>Variable 1</strong>" will display all the column names of the data uploaded by the user and the selectize input "<strong>Value</strong>" will display all the unique values of the corresponding column selected in "<strong>Variable 1</strong>". Ideally, the user will be able to add as many such rows ("<strong>Variable X</strong>" + "<strong>Value</strong>") as possible by some sort of trigger, one possibility being clicking the "Add more" action button.</p>
  
  <h2>A possible solution</h2>
  
  <p>After looking up online, I've found one promising solution given by <a href="https://gist.github.com/ncarchedi" rel="noreferrer"><strong>Nick Carchedi</strong></a> pasted below    </p>
  
  <p><code>ui.R</code></p>
  
  <pre><code>library(shiny)
  
  shinyUI(pageWithSidebar(
  
      # Application title
      headerPanel("Dynamically append arbitrary number of inputs"),
  
      # Sidebar with a slider input for number of bins
      sidebarPanel(
          uiOutput("allInputs"),
          actionButton("appendInput", "Append Input")
      ),
  
      # Show a plot of the generated distribution
      mainPanel(
          p("The crux of the problem is to dynamically add an arbitrary number of inputs
            without resetting the values of existing inputs each time a new input is added.
            For example, add a new input, set the new input's value to Option 2, then add
            another input. Note that the value of the first input resets to Option 1."),
  
          p("I suppose one hack would be to store the values of all existing inputs prior
            to adding a new input. Then,", code("updateSelectInput()"), "could be used to 
            return inputs to their previously set values, but I'm wondering if there is a 
            more efficient method of doing this.")
      )
  ))
  </code></pre>
  
  <p><code>server.R</code></p>
  
  <pre><code>library(shiny)
  
  shinyServer(function(input, output) {
  
      # Initialize list of inputs
      inputTagList &lt;- tagList()
  
      output$allInputs &lt;- renderUI({
          # Get value of button, which represents number of times pressed
          # (i.e. number of inputs added)
          i &lt;- input$appendInput
          # Return if button not pressed yet
          if(is.null(i) || i &lt; 1) return()
          # Define unique input id and label
          newInputId &lt;- paste0("input", i)
          newInputLabel &lt;- paste("Input", i)
          # Define new input
          newInput &lt;- selectInput(newInputId, newInputLabel,
                                  c("Option 1", "Option 2", "Option 3"))
          # Append new input to list of existing inputs
          inputTagList &lt;&lt;- tagAppendChild(inputTagList, newInput)
          # Return updated list of inputs
          inputTagList
      })
  
  })
  </code></pre>
  
  <h3>The downside</h3>
  
  <p>As pointed by <a href="https://gist.github.com/ncarchedi" rel="noreferrer"><strong>Nick Carchedi</strong></a> himself, all the existing input widgets will undesirably get reset every time when a new one is added.</p>
  
  <hr>
  
  <h2>A promising solution for data subsetting/filtering in Shiny</h2>
  
  <p>As suggested by <a href="https://stackoverflow.com/users/4190526/warmoverflow"><strong>warmoverflow</strong></a>, the <code>datatable</code> function in <a href="https://rstudio.github.io/DT/" rel="noreferrer"><strong>DT</strong></a> package provides a nice way to filter the data in Shiny. See below a minimal example with data filtering enabled.</p>
  
  <pre><code>library(shiny)
  shinyApp(
      ui = fluidPage(DT::dataTableOutput('tbl')),
      server = function(input, output) {
          output$tbl = DT::renderDataTable(
              iris, filter = 'top', options = list(autoWidth = TRUE)
          )
      }
  )
  </code></pre>
  
  <p>If you are going to use it in your Shiny app, there are some important aspects that are worth noting.</p>
  
  <ol>
  <li>Filtering box type
  
  <ul>
  <li>For numeric/date/time columns: range sliders are used to filter rows within ranges</li>
  <li>For factor columns: selectize inputs are used to display all possible categories</li>
  <li>For character columns: ordinary search boxes are used</li>
  </ul></li>
  <li>How to obtain the filtered data
  
  <ul>
  <li>Suppose the table output id is <code>tableId</code>, use <code>input$tableId_rows_all</code> as the indices of rows on all pages (after the table is filtered by the search strings). <strong>Please note that <code>input$tableId_rows_all</code> returns the indices of rows on all pages for DT (>= 0.1.26). If you use the DT version by regular <code>install.packages('DT')</code>, only the indices of the current page are returned</strong></li>
  <li>To install <strong>DT</strong> (>= 0.1.26), refer to its <a href="https://github.com/rstudio/DT" rel="noreferrer">GitHub page</a></li>
  </ul></li>
  <li>Column width
  
  <ul>
  <li>If the data have many columns, column width and filter box width will be narrow, which makes it hard to see the text as report <a href="https://stackoverflow.com/questions/36164448/r-shiny-datatable-filter-box-size-to-narrow-to-see-text">here</a></li>
  </ul></li>
  </ol>
  
  <hr>
  
  <h2>Still to be solved</h2>
  
  <p>Despite some known issues, <code>datatable</code> in <a href="https://rstudio.github.io/DT/" rel="noreferrer"><strong>DT</strong></a> package stands as a promising solution for data subsetting in Shiny. The question itself, i.e. how to dynamically append arbitrary number of input widgets in Shiny, nevertheless, is interesting and also challenging. Until people find a good way to solve it, I will leave this question open :)</p>
  
  <p>Thank you!</p>