通过 insertUI 重置动态生成的小部件的输入字段

Reset input fields of dynamically generated widgets through insertUI

我正在尝试开发一个 shinyapp,它是更大表单的一部分。特别是,对于这个模块,用户可以动态添加(通过插入UI)和删除(通过删除UI)一个新行,其中的小部件与前一个完全相同。此外,每个新小部件都将包含与前一行小部件完全相同的选择,感谢 Eli Berkow 找到了最后一个功能的解决方案。

理想情况下,在用户提交他的答案后,调用 shinyjs 包的 reset() 函数,放置在 observeEvent() 中以重置每个字段的输入。 这适用于 "static" 小部件(未包含在此示例中),但我找不到重置动态生成的小部件的输入字段的方法。

我试图将每一行分配给不同的 div() 并且我在我的 observeEvent() 中使用了一个 for 循环来重置每个 div() 中存在的输入,但是只是清理了一些字段。此外,理想情况下,如果按钮不仅可以重置字段,还可以删除用户添加的每一行并从 0 重新开始计数,那就更好了。下面有一些带有 Reset Button 的代码已插入 UI 但仍然没有任何功能。


library(shiny)
library(shinyjs)

###= UI
ui <- fluidPage(

  br(),

  useShinyjs(),

  br(),

  fluidRow(
    column(width = 3,
           align = "center",
           h5("Letters")
    ),
    column(width = 3,
           align = "center",
           h5("Numbers")
    )
  ),

  br(),

  fluidRow(
    column(width = 12,
           tags$div(id = "amr_test_placeholder")
    )
  ),

  fluidRow(

    br(),
    # "+" and "-" buttons
    column(width = 12,
           actionButton(inputId = "add_amr_test",
                        label = icon(name = "plus",
                                     lib = "font-awesome")),
           actionButton(inputId = "remove_amr_test",
                        label = icon(name = "times",
                                     lib = "font-awesome")),
           actionButton(inputId = "reset_button",
                        label = "RESET FIELDS")
    )
  ),

  br(),
  br(),

  fluidRow(
    column(width = 6,
           tableOutput(outputId = "show_table")
    )
  )

)

###= SERVER
server <- function(input, output, session) {

  observe({

    toggleState(id = "remove_amr_test",
                condition = input$add_amr_test >= input$remove_amr_test)

  })



  amr_test_values <- reactiveValues(val = 0,
                                    ignore = 0)


  ### Defined the input number to count every row
  input_number <- reactive({

    input_number <- input$add_amr_test - input$remove_amr_test + 1

    return(input_number)

  })




  observeEvent(input$add_amr_test, {

    amr_test_divId <- length(amr_test_values$val) + 1

    ###= Animal species
    if (!is.null(eval(parse(text = paste0("input$letters_", input_number() - 1))))) {

      letters_value = eval(parse(text = paste0("input$letters_", input_number() - 1)))

    } else {

      letters_value = ""

    }

    ###= Animal sample type
    if (!is.null(eval(parse(text = paste0("input$numbers_", input_number() - 1))))) {

      numbers_value = eval(parse(text = paste0("input$numbers_", input_number() - 1)))

    } else {

      numbers_value = ""

    }

    ###= Insert dynamic UI
    insertUI(
      selector = "#amr_test_placeholder",
      where = "beforeBegin",
      ui = tags$div(id = amr_test_divId,
                    tags$head(tags$style(HTML(".shiny-split-layout > div {overflow: visible;}"))),

                    br(),

                    fluidRow(
                      column(width = 3,
                             splitLayout(cellWidths = c("18%", "82%"),
                                         h5(paste0(input_number(), ". ")),
                                         selectizeInput(inputId = paste0("letters_",
                                                                         input_number()),
                                                        label =  NULL,
                                                        choices = c("A" = "",
                                                                    "A",
                                                                    "B",
                                                                    "C",
                                                                    "D",
                                                                    "E",
                                                                    "F",
                                                                    "F"),
                                                        selected = letters_value,
                                                        width = "100%",
                                                        options = list(create = TRUE))
                             )
                      ),
                      column(width = 3,
                             selectizeInput(inputId = paste0("numbers_",
                                                             input_number()),
                                            label = NULL,
                                            choices = list("1" = "",
                                                           "1" = "1",
                                                           "2" = "2",
                                                           "3" = "3",
                                                           "4" = "4",
                                                           "5" = "5"),
                                            selected = numbers_value,
                                            width = "100%")
                      )
                    )
      )
    )

    amr_test_values$val <- c(amr_test_values$val,
                             amr_test_divId)

  }, ignoreNULL = FALSE)

  ###= Remove dynamic UI
  observeEvent(input$remove_amr_test, {

    removeUI(

      selector = paste0('#', amr_test_values$val[length(amr_test_values$val)])

    )

    amr_test_values$val <- amr_test_values$val[-length(amr_test_values$val)]

  })






  ###= Dataset to check the dynamic updates of the responses
  response <- reactive({

    if (input_number() != 0) {

      response <- data.frame("Letters" = sapply(X = 1:input_number(),
                                                FUN = function(i) {

                                                  input[[paste0("letters_", i)]]

                                                }),
                             "Numbers" = sapply(X = 1:input_number(),
                                                FUN = function(i) {

                                                  input[[paste0("numbers_", i)]]

                                                })

      )

      return(response)

    } else {

      response <- NULL

    }

  })



  output$show_table <- renderTable({

    response()

  })

}



###= Launch App
shinyApp(ui = ui, server = server)

预期结果见下link.

谢谢!

编辑:

Eli Berkow 对我之前版本的问题给出的答案完美无缺,所以我将其标记为正确。

我更新了代码,以便了解当应用程序启动时第一行小部件自动显示时 Eli 的脚本应该如何工作。我一直不明白是否要修改与 reactiveValues 或最后一个 observeEvent 相关的内容。因此,在这个版本的脚本中,我添加了自动显示第一行的功能。什么会适配Eli的代码?

p.s.: 我在小部件下添加了一个反应数据集,以实际查看值的存储方式(只是检查以了解脚本是否正常工作)。

谢谢!

见下文:

library(shiny)
library(shinyjs)

###= UI
ui <- fluidPage(

    br(),

    useShinyjs(),

    br(),

    fluidRow(
        column(width = 3,
               align = "center",
               h5("Letters")
        ),
        column(width = 3,
               align = "center",
               h5("Numbers")
        )
    ),

    br(),

    fluidRow(
        column(width = 12,
               tags$div(id = "amr_test_placeholder")
        )
    ),

    fluidRow(

        br(),
        # "+" and "-" buttons
        column(width = 12,
               actionButton(inputId = "add_amr_test",
                            label = icon(name = "plus",
                                         lib = "font-awesome")),
               actionButton(inputId = "remove_amr_test",
                            label = icon(name = "times",
                                         lib = "font-awesome")),
               actionButton(inputId = "reset_button",
                            label = "RESET FIELDS")
        )
    ),

    br(),
    br()

)

###= SERVER
server <- function(input, output, session) {

    observe({

        toggleState(id = "remove_amr_test",
                    condition = input$add_amr_test > input$remove_amr_test + amr_test_values$reset)

        toggleState(id = "reset_button",
                condition = input$add_amr_test > input$remove_amr_test + amr_test_values$reset)

    })



    amr_test_values <- reactiveValues(val = 0,
                                      reset = 0)


    ### Defined the input number to count every row
    input_number <- reactive({

        input_number <- input$add_amr_test - input$remove_amr_test - amr_test_values$reset

        return(input_number)

    })




    observeEvent(input$add_amr_test, {

        amr_test_divId <- length(amr_test_values$val) + 1

        ###= Animal species
        if (!is.null(eval(parse(text = paste0("input$letters_", input_number() - 1))))) {

            letters_value = eval(parse(text = paste0("input$letters_", input_number() - 1)))

        } else {

            letters_value = ""

        }

        ###= Animal sample type
        if (!is.null(eval(parse(text = paste0("input$numbers_", input_number() - 1))))) {

            numbers_value = eval(parse(text = paste0("input$numbers_", input_number() - 1)))

        } else {

            numbers_value = ""

        }

        ###= Insert dynamic UI
        insertUI(
            selector = "#amr_test_placeholder",
            where = "beforeBegin",
            ui = tags$div(id = amr_test_divId,
                          tags$head(tags$style(HTML(".shiny-split-layout > div {overflow: visible;}"))),

                          br(),

                          fluidRow(
                              column(width = 3,
                                     splitLayout(cellWidths = c("18%", "82%"),
                                                 h5(paste0(input_number(), ". ")),
                                                 selectizeInput(inputId = paste0("letters_",
                                                                                 input_number()),
                                                                label =  NULL,
                                                                choices = c("A" = "",
                                                                            "A",
                                                                            "B",
                                                                            "C",
                                                                            "D",
                                                                            "E",
                                                                            "F",
                                                                            "F"),
                                                                selected = letters_value,
                                                                width = "100%",
                                                                options = list(create = TRUE))
                                     )
                              ),
                              column(width = 3,
                                     selectizeInput(inputId = paste0("numbers_",
                                                                     input_number()),
                                                    label = NULL,
                                                    choices = list("1" = "",
                                                                   "1" = "1",
                                                                   "2" = "2",
                                                                   "3" = "3",
                                                                   "4" = "4",
                                                                   "5" = "5"),
                                                    selected = numbers_value,
                                                    width = "100%")
                              )
                          )
            )
        )

        amr_test_values$val <- c(amr_test_values$val,
                                 amr_test_divId)

    })

    ###= Remove dynamic UI
    observeEvent(input$remove_amr_test, {

        removeUI(

            selector = paste0('#', amr_test_values$val[length(amr_test_values$val)])

        )

        amr_test_values$val <- amr_test_values$val[-length(amr_test_values$val)]

    })

    observeEvent(input$reset_button, {
        for(remove_value in 2:max(amr_test_values$val)) {
            removeUI(

                selector = paste0('#', remove_value)

            )
        }

        amr_test_values$reset <- input$add_amr_test - input$remove_amr_test
        amr_test_values$val <- 0
    })

}


###= Launch App
shinyApp(ui = ui, server = server)

我在这里添加了一个reset反应值:

observe({

    toggleState(id = "remove_amr_test",
                condition = input$add_amr_test > input$remove_amr_test + amr_test_values$reset)

    toggleState(id = "reset_button",
                condition = input$add_amr_test > input$remove_amr_test + amr_test_values$reset)

})



amr_test_values <- reactiveValues(val = 0,
                                  reset = 0)


### Defined the input number to count every row
input_number <- reactive({

    input_number <- input$add_amr_test - input$remove_amr_test - amr_test_values$reset

    return(input_number)

})

然后这个 observeEvent:

observeEvent(input$reset_button, {
    for(remove_value in 2:max(amr_test_values$val)) {
        removeUI(

            selector = paste0('#', remove_value)

        )
    }

    amr_test_values$reset <- input$add_amr_test - input$remove_amr_test
    amr_test_values$val <- 0
})

更新

基于您的编辑。

library(shiny)
library(shinyjs)

###= UI
ui <- fluidPage(

  br(),

  useShinyjs(),

  br(),

  fluidRow(
    column(width = 3,
           align = "center",
           h5("Letters")
    ),
    column(width = 3,
           align = "center",
           h5("Numbers")
    )
  ),

  br(),

  fluidRow(
    column(width = 12,
           tags$div(id = "amr_test_placeholder")
    )
  ),

  fluidRow(

    br(),
    # "+" and "-" buttons
    column(width = 12,
           actionButton(inputId = "add_amr_test",
                        label = icon(name = "plus",
                                     lib = "font-awesome")),
           actionButton(inputId = "remove_amr_test",
                        label = icon(name = "times",
                                     lib = "font-awesome")),
           actionButton(inputId = "reset_button",
                        label = "RESET FIELDS")
    )
  ),

  br(),
  br(),

  fluidRow(
    column(width = 6,
           tableOutput(outputId = "show_table")
    )
  )

)

###= SERVER
server <- function(input, output, session) {

  observe({

    toggleState(id = "remove_amr_test",
                condition = input$add_amr_test > input$remove_amr_test + amr_test_values$reset)

    toggleState(id = "reset_button",
                condition = input$add_amr_test > input$remove_amr_test + amr_test_values$reset)

  })



  amr_test_values <- reactiveValues(val = 0,
                                    reset = 0)


  ### Defined the input number to count every row
  input_number <- reactive({

    input_number <- input$add_amr_test - input$remove_amr_test - amr_test_values$reset + 1

    return(input_number)

  })




  observeEvent(input$add_amr_test, {

    amr_test_divId <- length(amr_test_values$val) + 1

    ###= Animal species
    if (!is.null(eval(parse(text = paste0("input$letters_", input_number() - 1))))) {

      letters_value = eval(parse(text = paste0("input$letters_", input_number() - 1)))

    } else {

      letters_value = "A"

    }

    ###= Animal sample type
    if (!is.null(eval(parse(text = paste0("input$numbers_", input_number() - 1))))) {

      numbers_value = eval(parse(text = paste0("input$numbers_", input_number() - 1)))

    } else {

      numbers_value = 1

    }

    ###= Insert dynamic UI
    insertUI(
      selector = "#amr_test_placeholder",
      where = "beforeBegin",
      ui = tags$div(id = amr_test_divId,
                    tags$head(tags$style(HTML(".shiny-split-layout > div {overflow: visible;}"))),

                    br(),

                    fluidRow(
                      column(width = 3,
                             splitLayout(cellWidths = c("18%", "82%"),
                                         h5(paste0(input_number(), ". ")),
                                         selectizeInput(inputId = paste0("letters_",
                                                                         input_number()),
                                                        label =  NULL,
                                                        choices = c("A" = "",
                                                                    "A",
                                                                    "B",
                                                                    "C",
                                                                    "D",
                                                                    "E",
                                                                    "F",
                                                                    "F"),
                                                        selected = letters_value,
                                                        width = "100%",
                                                        options = list(create = TRUE))
                             )
                      ),
                      column(width = 3,
                             selectizeInput(inputId = paste0("numbers_",
                                                             input_number()),
                                            label = NULL,
                                            choices = list("1" = "",
                                                           "1" = "1",
                                                           "2" = "2",
                                                           "3" = "3",
                                                           "4" = "4",
                                                           "5" = "5"),
                                            selected = numbers_value,
                                            width = "100%")
                      )
                    )
      )
    )

    amr_test_values$val <- c(amr_test_values$val,
                             amr_test_divId)

  }, ignoreNULL = FALSE)

  ###= Remove dynamic UI
  observeEvent(input$remove_amr_test, {

    removeUI(

      selector = paste0('#', amr_test_values$val[length(amr_test_values$val)])

    )

    amr_test_values$val <- amr_test_values$val[-length(amr_test_values$val)]

  })


  observeEvent(input$reset_button, {
    for(remove_value in 3:max(amr_test_values$val)) {
      removeUI(

        selector = paste0('#', remove_value)

      )
    }

    amr_test_values$reset <- input$add_amr_test - input$remove_amr_test
    amr_test_values$val <- c(0, 2)
  })



  ###= Dataset to check the dynamic updates of the responses
  response <- reactive({

    if (input_number() != 0 & !is.null(input[[paste0("letters_", input_number())]])) {

      response <- data.frame("Letters" = sapply(X = 1:input_number(),
                                                FUN = function(i) {

                                                  input[[paste0("letters_", i)]]

                                                }),
                             "Numbers" = sapply(X = 1:input_number(),
                                                FUN = function(i) {

                                                  input[[paste0("numbers_", i)]]

                                                })

      )

      return(response)

    } else {

      response <- NULL

    }

  })



  output$show_table <- renderTable({

    response()

  })

}



###= Launch App
shinyApp(ui = ui, server = server)