Reset input fields of dynamically generated widget

2019-08-21 15:55发布

问题:

I am trying to develop a shinyapp that is part of a bigger form. In particular, for this module the users have the possibility to dynamically add (through insertUI) and remove (through removeUI) a new row with the exact same widgets of the previous one. Moreover, every new widget will contain the exact selected same choices of the widgets of the previous row, credits to Eli Berkow for having found the solution for this last functionality.

Ideally, after a users submit his answer, a reset() function of the shinyjs package, placed inside an observeEvent() is called to reset the input of every field. This works fine with the "static" widget (that are not included in this example), but I can't find a way to reset the input fields of the dynamically generated widget.

I tried to assign every row to a different div() and I used a for loop inside my observeEvent() to reset the inputs present inside every div(), but just some fields are cleaned. Moreover, ideally would be better if the button could not only just reset the fields, but delete every row added by the user and restart the numeration from 0. Below there's some code with a Reset Button inserted in the UI but still with no functionality.


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)

The expected result can be seen at the following link.

Thanks!

EDIT:

The answer given by Eli Berkow to my previous version of the question works perfectly so I marked it as correct.

I have updated the code in order to understand how Eli's script should work when the first row of widget is automatically displayed when the application starts. I haven't understood if to modify something related to the reactiveValues or the last observeEvent. Hence, in this version of the script I added the functions to automatically display the first row. What will adapt Eli's code?

p.s.: I have added a reactive dataset under the widget to actually see how the values are stored well or not (just a check to understand if the script works fine).

Thanks!

回答1:

See below:

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)

I added a reset reactive value here:

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)

})



observe({

    toggleState(id = "remove_amr_test",
                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)

})

and then this 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
})