简体   繁体   中英

R Shiny store the user input from multiple dynamically generated textAreaInput fields in an object in the server part

New to shiny and struggling with this for more than two days now. I have created an application where the user loads .csv data file and chooses one or more variables whose names appear in the application as check boxes. When a checkbox is checked, a new checkbox appears under with the same name and when it is clicked too, a textAreaInput appears next to it where the user can add variable names that constitute the target variable as a scale. Here is an oversimplified version of the application:

library(shiny)

ui <- fluidPage(

  mainPanel(
    fileInput(inputId = "file", label = "Choose File", multiple = TRUE, accept = ".csv"),
    uiOutput(outputId = "varCheckBoxesIndivScores"),

    column(width = 3,
           uiOutput(outputId = "selectedScoresCheckBoxes")),

    conditionalPanel(condition = "input.selectedScoresCheckBoxes",
                     column(width = 6,
                            uiOutput(outputId = "variablesConstitutingScale"))
    )
  )
)

server = function(input, output, session) {

  df <- reactive({
    if(is.null(input$file)) {
      return(NULL)
    } else {
      tbl <- fread(input$file$datapath, stringsAsFactors = TRUE)
      return(tbl)
    }
  })

  output$varCheckBoxesIndivScores <- renderUI({
    if(is.null(df())) {
      return(NULL)
    } else if(!is.null(df())) {
      return(tags$div(align = "left",
                      class = "multicol",
                      checkboxGroupInput(inputId = "varCheckBoxesIndivScores",
                                         label = "Select variables",
                                         choices = colnames(df()))))
    }
  })

  output$selectedScoresCheckBoxes <- renderUI({
    if(is.null(df())) {
      return(NULL)
    } else if(!is.null(df())) {
      return(tags$div(align = "left",
                      checkboxGroupInput(inputId = "selectedScoresCheckBoxes",
                                         label = "",
                                         choices = input$varCheckBoxesIndivScores)))
    }
  })

  output$variablesConstitutingScale <- renderUI({
    if(is.null(df())) {
      return(NULL)
    } else if(!is.null(df()) & length(input$selectedScoresCheckBoxes > 0)) {
      var.list.input.fields <- lapply(input$selectedScoresCheckBoxes, function(i) {
        textAreaInput(inputId = "i", label = paste("Variables constituting scale", i), width = "700px", height = "100px", value = NULL)
      })
      var.list.input.fields
    }
  })

}

shinyApp(ui = ui, server = server)

The data to load is generated like this (just an excerpt, the real one has more columns and cases):

library(data.table)

x <- data.table(ID = c(2201:2220), VAR1 = rnorm(n = 20, mean = 10, sd = 2),
VAR2 = rnorm(n = 20, mean = 100, sd = 20), VAR3 = 1:20, VAR4 = 21:40,
VAR5 = 41:60, VAR6 = 61:80, VAR7 = 81:100)

write.csv(x = x, file = "/tmp/test_data.csv", row.names = FALSE)

It works fine, no errors. Here is how it looks, after I enter the variable names in each of the generated textAreaInput fields: 在此处输入图片说明

However, I would like to take the user input from each dynamically generated textAreaInput and store it in a list like:

list(VAR1 = "VAR3 VAR4 VAR5", VAR2 = "VAR6 VAR7")

or

list(VAR1 = "VAR3", "VAR4", "VAR5", VAR2 = "VAR6", "VAR7")

inside the server part of the application for future use.

I tried to follow the solution in this thread, but I did not succeed to come to any solution and feel quite confused. Can someone help?

First, you want to make sure to assign each of your dynimcally added elements to have a unique name. You have just hard coded the letter "i" in the sample. You want something like

textAreaInput(inputId = paste0("varconst_",i), label = paste("Variables constituting scale", i), 
    width = "700px", height = "100px", value = NULL)

Then you can observe those text boxes with something like this

observeEvent(lapply(paste0("varconst_", input$selectedScoresCheckBoxes), function(x) input[[x]]), {
  obj <- Map(function(x) input[[paste0("varconst_",x)]], input$selectedScoresCheckBoxes)
  dput(obj)
})

Here I just used dput to dump the list to the console so you can see it as it gets updated but you can do whatever you want with that.

I have modified the code of the application as per MrFlick's answer. To leave a paper trail of the complete solution, I am posting it below. The few additional modifications I have made include the printout of the list with the variables for each of the generated textAreaInput fields, so that the list can be viewed in the application itself. I have also added some further modifications of the obj , after it is generated, to obtain the list as desired.

If there are more dynamically generated output sections where check boxes and related text areas, the varconst_ index has to be made unique across the different chunks of code (eg varconst1_ , varconst2_ , varconst3_ , etc.).

Here is the code:

library(shiny)

ui <- fluidPage(

  mainPanel(
    fileInput(inputId = "file", label = "Choose File", multiple = TRUE, accept = ".csv"),
    uiOutput(outputId = "varCheckBoxesIndivScores"),

    fluidRow(
      column(width = 3,
             uiOutput(outputId = "selectedScoresCheckBoxes")),

      conditionalPanel(condition = "input.selectedScoresCheckBoxes",
                       column(width = 6,
                              uiOutput(outputId = "variablesConstitutingScale")))),
    br(),

    fluidRow(
      conditionalPanel(condition = "input.selectedScoresCheckBoxes",
                       verbatimTextOutput(outputId = "scalesVarList")))
  )
)

server = function(input, output, session) {

  df <- reactive({
    if(is.null(input$file)) {
      return(NULL)
    } else {
      tbl <- fread(input$file$datapath, stringsAsFactors = TRUE)
      return(tbl)
    }
  })

  output$varCheckBoxesIndivScores <- renderUI({
    if(is.null(df())) {
      return(NULL)
    } else if(!is.null(df())) {
      return(tags$div(align = "left",
                      class = "multicol",
                      checkboxGroupInput(inputId = "varCheckBoxesIndivScores",
                                         label = "Select variables",
                                         choices = colnames(df()))))
    }
  })

  output$selectedScoresCheckBoxes <- renderUI({
    if(is.null(df())) {
      return(NULL)
    } else if(!is.null(df())) {
      return(tags$div(align = "left",
                      checkboxGroupInput(inputId = "selectedScoresCheckBoxes",
                                         label = "",
                                         choices = input$varCheckBoxesIndivScores)))
    }
  })

  output$variablesConstitutingScale <- renderUI({
    if(is.null(df())) {
      return(NULL)
    } else if(!is.null(df()) & length(input$selectedScoresCheckBoxes > 0)) {
      var.list.input.fields <- lapply(input$selectedScoresCheckBoxes, function(i) {
        textAreaInput(inputId = paste0("varconst_",i), label = paste("Variables constituting scale", i), 
                      width = "700px", height = "100px", value = NULL)
      })
      var.list.input.fields
    }
  })

  observeEvent(lapply(paste0("varconst_", input$selectedScoresCheckBoxes), function(x) input[[x]]), {
    obj <- Map(function(x) input[[paste0("varconst_",x)]], input$selectedScoresCheckBoxes)
    obj <- sapply(obj, function(i) {
      if(length(i) > 0) {
        strsplit(x = i, split = " ")
      }
    })
    dput(obj)
    output$scalesVarList <- renderPrint({
      if(is.null(df())) {
        return(NULL)
      } else if(!is.null(df()) && length(input$selectedScoresCheckBoxes) > 0 && length(obj) > 0) {
        print(obj)
      }
    })
  })

}

shinyApp(ui = ui, server = server)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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