问题
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)
})
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
})
Update
Based on your edit.
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)
来源:https://stackoverflow.com/questions/57535799/reset-input-fields-of-dynamically-generated-widgets-through-insertui