0

I am trying to create the possibility for users to dynamically add new columns to a dataframe in a Shiny app whenever the action button is pressed.

I found a solution on how to dynamically add user input fields in Shiny on another stack overflow thread.

Now, I am trying to modify this code in order to dynamically add columns to a dataframe. My part of the code is commented out:

library(shiny)

ui <- shinyUI(fluidPage(
  
  sidebarPanel(
    
    actionButton("add_btn", "Add Textbox"),
    actionButton("rm_btn", "Remove Textbox"),
    textOutput("counter")
    # ,tableOutput("table")
    
  ),
  
  mainPanel(uiOutput("textbox_ui"))
  
))

server <- shinyServer(function(input, output, session) {
  
  # Track the number of input boxes to render
  counter <- reactiveValues(n = 0)
  
  # Track all user inputs
  AllInputs <- reactive({
    x <- reactiveValuesToList(input)
  })
  
  observeEvent(input$add_btn, {counter$n <- counter$n + 1})
  observeEvent(input$rm_btn, {
    if (counter$n > 0) counter$n <- counter$n - 1
  })
  
  output$counter <- renderPrint(print(counter$n))
  
  textboxes <- reactive({
    
    n <- counter$n
    
    if (n > 0) {
      isolate({
        lapply(seq_len(n), function(i) {
          textInput(inputId = paste0("textin", i),
                    label = paste0("Textbox", i), 
                    value = AllInputs()[[paste0("textin", i)]])
        })
      })
    }
    
  })
  
  # df <- reactive({
  #   n <- counter$n
  # 
  #   df <- data.frame(placeholder=NA)
  #   
  #   lapply(seq_len(n), function(i){
  #   df[[paste0("col", i)]] <- paste0("input$textin", i)
  #   }
  #   )
  #   df
  # })
  
output$textbox_ui <- renderUI({ textboxes() })
  
# output$table <- renderTable({
#   df()
#   
# })

  
})

shinyApp(ui, server)

Can someone help how to dynamically add a column to a dataframe whenever the actionButton ("add_btn") is pressed so that the input is filled into the new column. I found solutions that add rows, but I did not find any posts that help how to add columns.

Edit: Depending on the number of TextInputs, I want to create a number of columns with the information that was filled in textInput function. For example: If a user adds three textbooks with this information:

  • Textbox 1: "A"
  • Textbox 2: "B"
  • Textbox 3: "C"

then I want the created dataftame to look like this:

df <- data.frame(Textbox1="A", Textbox2="B", Textbox3="C")
2
  • Can you clarify what exactly your aim is? The code you provided adds and removes Text Inputs to the UI. But you want to add a column to your dataframe? What should be the name, content of the column? Commented Feb 3, 2022 at 13:59
  • Thank you for your comment. I edited my post and hope to have clarified. Commented Feb 3, 2022 at 14:20

1 Answer 1

1

Perhaps you are looking for this

library(shiny)
library(tidyr)
library(dplyr)

ui <- shinyUI(fluidPage(
  
  sidebarPanel(
    
    actionButton("add_btn", "Add Textbox"),
    actionButton("rm_btn", "Remove Textbox"),
    textOutput("counter")
    
  ),
  
  mainPanel(uiOutput("textbox_ui")
            ,tableOutput("table")
            )
  
))

server <- shinyServer(function(input, output, session) {
  
  # Track the number of input boxes to render
  counter <- reactiveValues(n = 0, df=NULL)
  
  # Track all user inputs
  AllInputs <- reactive({
    x <- reactiveValuesToList(input)
  })
  
  observeEvent(input$add_btn, {
    counter$n <- counter$n + 1
  })
  observeEvent(input$rm_btn, {
    
    if (counter$n > 0) counter$n <- counter$n - 1
    
  })
  
  output$counter <- renderPrint(print(counter$n))
  
  textboxes <- reactive({
    
    n <- counter$n
    
    if (n > 0) {
      isolate({
        lapply(seq_len(n), function(i) {
          textInput(inputId = paste0("textin", i),
                    label = paste0("Textbox", i), 
                    value = AllInputs()[[paste0("textin", i)]])
        })
      })
    }
    
  })
  
  output$textbox_ui <- renderUI({ textboxes() })
  
  df1 <- data.frame()
  
  observe({
    n <- counter$n
    if (n > 0) {
      df1 <- data.frame()
      
        lapply(1:n, function(i){
          req(input[[paste0("textin", i)]])
          
          df1 <<- rbind(df1,data.frame(input[[paste0("textin", i)]]))
          
        })
    }
    
    counter$df <- df1
  })
  
  output$table <- renderTable({
    if (is.null(counter$df)) return(NULL)
    else {
      n <- nrow(counter$df)
      if (n>0) {
        colnames(counter$df) <- "values"
        df1 <- counter$df %>% mutate(row=row_number())
        df1 %>% pivot_wider(names_prefix="Textbox", names_from = row, values_from = values)
      }else return(NULL)
    }
  })
  
  
})

shinyApp(ui, server)
Sign up to request clarification or add additional context in comments.

1 Comment

That's exactly what I am looking for! Thank you so much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.