更改输入值时不要清除(用户输入的)反应变量的值
Do not wipe out (user entered) values of reactive variables when changing input values
我想使用附加的演示代码收集个人信息(姓名和年龄)作为用户输入。初始个体数为1,当用户increases/decreases个体数的值时,会反应add/reduce对应行收集个体信息。我的问题是如何避免在increasing/decreasing个人的数量时抹掉当前输入的信息。例如截图1显示的是用户(我)输入的个人1的信息。当我将个人数量的值更改为2时,我之前输入的信息被抹掉了(截图2),我不得不重新输入个人1的信息。什么我希望当我将个人数量增加到 2 时,会像 屏幕截图 3(个人 1 的信息不会被删除)。任何人都可以帮我解决这个问题吗?谢谢!
更新于 2022 年 5 月 15 日
使用 isolate
的更新答案正是我要找的。再次感谢@jpdugo17 的帮助。
示例代码
library(shiny)
ui <- fluidPage(
tabsetPanel(
tabPanel(
h4("Individual Information"),
fluidRow(column(4,numericInput("ninds",
label = "Number of individuals",
value = 1, min = 1, step = 0.5, width = "300px"))),
br(),
fluidRow(column(2,align = "center",strong("Individual #")),
column(5,align = "center",strong("Individual Name")),
column(5,align = "center",strong("Age"))),
fluidRow(
column(2,wellPanel(uiOutput("indNum"))),
column(5,wellPanel(uiOutput("Name"))),
column(5,wellPanel(uiOutput("Age"))))
)
)
)
server <- function(input, output) {
# create reactive variable paste0("individualNum", i) for further using
output$indNum <- renderUI({
num <- as.integer(input$ninds)
req(num)
lapply(1:num, function(i) {
numericInput(paste0("individualNum", i), value = i, label = "", max = i, min = i)
})
})
# create reactive variable paste0("name", i) for further using
output$Name <- renderUI({
num <- as.integer(input$ninds)
req(num)
lapply(1:num, function(i) {
textInput(paste0("name", i), label = "")
})
})
# create reactie variable paste0("age", i) for further using
output$Age <- renderUI({
num <- as.integer(input$ninds)
req(num)
lapply(1:num, function(i) {
numericInput(paste0("age", i), label = "", value = 0)
})
})
}
# Run the app ----
shinyApp(ui, server)
截图-1
截图2
截图3
编辑:
我们可以将 isolate
与输入的当前值一起使用,并将其作为 numericInput
或 textInput
中的值参数传递。这将起作用,因为任何尚不存在的输入都会产生 NULL
.
server <- function(input, output) {
# create reactive variable paste0("individualNum", i) for further using
num <- reactive({
req(input$ninds)
input$ninds
})
output$indNum <- renderUI({
lapply(1:num(), function(i) {
numericInput(paste0("individualNum", i), value = i, label = "", max = i, min = i)
})
})
# create reactive variable paste0("name", i) for further using
output$Name <- renderUI({
lapply(1:num(), function(i) {
textInput(paste0("name", i), label = "", value = isolate(input[[paste0("name", i)]]))
})
})
# create reactie variable paste0("age", i) for further using
output$Age <- renderUI({
lapply(1:num(), function(i) {
numericInput(paste0("age", i), label = "", value = isolate(input[[paste0("age", i)]]))
})
})
}
原始答案使用 insertUI
:
我们可以实现这样的逻辑:创建一个计数器来存储当前输入的数字,制作两个按钮,一个用于添加,另一个用于删除输入。我们必须用具有唯一 ID 的 div 包装每个输入(因为输入函数通常会添加多个元素)。
library(shiny)
library(purrr)
ui <- fluidPage(
tabsetPanel(
tabPanel(
h4("Individual Information"),
fluidRow(
column(
4, fluidRow(
column(6, numericInput("ninds",
label = "Number of individuals",
value = 1, min = 1, step = 0.5, width = "300px"
)), column(3, actionButton("add_ui", "Add Individual", style = "background-color: green;")),
column(3, actionButton("remove_ui", "Remove Individual", style = "background-color: red;"))
)
)
),
br(),
fluidRow(
column(2, align = "center", strong("Individual #")),
column(5, align = "center", strong("Individual Name")),
column(5, align = "center", strong("Age"))
),
fluidRow(
column(2, wellPanel(id = "IndNumber")),
column(5, wellPanel(id = "Name")),
column(5, wellPanel(id = "Age"))
)
)
)
)
server <- function(input, output) {
# track the number of inputs
ui_counter <- reactiveVal(1)
observeEvent(input$add_ui, {
div_nms <- map_chr(c("individualNum", "name", "age"), ~ paste0("div", .x, ui_counter()))
# individual number
insertUI(
selector = "#IndNumber",
ui = div(
id = div_nms[[1]],
numericInput(paste0("individualNum", ui_counter()),
label = "",
value = ui_counter(),
min = ui_counter(),
max = ui_counter()
)
)
)
# name input
insertUI(
selector = "#Name",
ui = div(id = div_nms[[2]], textInput(paste0("name", ui_counter()),
label = ""
))
)
# age input
insertUI(
selector = "#Age",
ui = div(id = div_nms[[3]], numericInput(paste0("age", ui_counter()),
label = "",
value = 0
))
)
ui_counter(ui_counter() + 1)
})
# observer to remove the divs containing the inputs
observeEvent(input$remove_ui, {
if (ui_counter() > 1) {
walk(c("individualNum", "name", "age"), ~ removeUI(paste0("#div", .x, ui_counter() - 1)))
ui_counter(ui_counter() - 1)
}
})
}
# Run the app ----
shinyApp(ui, server)
我想使用附加的演示代码收集个人信息(姓名和年龄)作为用户输入。初始个体数为1,当用户increases/decreases个体数的值时,会反应add/reduce对应行收集个体信息。我的问题是如何避免在increasing/decreasing个人的数量时抹掉当前输入的信息。例如截图1显示的是用户(我)输入的个人1的信息。当我将个人数量的值更改为2时,我之前输入的信息被抹掉了(截图2),我不得不重新输入个人1的信息。什么我希望当我将个人数量增加到 2 时,会像 屏幕截图 3(个人 1 的信息不会被删除)。任何人都可以帮我解决这个问题吗?谢谢!
更新于 2022 年 5 月 15 日
使用 isolate
的更新答案正是我要找的。再次感谢@jpdugo17 的帮助。
示例代码
library(shiny)
ui <- fluidPage(
tabsetPanel(
tabPanel(
h4("Individual Information"),
fluidRow(column(4,numericInput("ninds",
label = "Number of individuals",
value = 1, min = 1, step = 0.5, width = "300px"))),
br(),
fluidRow(column(2,align = "center",strong("Individual #")),
column(5,align = "center",strong("Individual Name")),
column(5,align = "center",strong("Age"))),
fluidRow(
column(2,wellPanel(uiOutput("indNum"))),
column(5,wellPanel(uiOutput("Name"))),
column(5,wellPanel(uiOutput("Age"))))
)
)
)
server <- function(input, output) {
# create reactive variable paste0("individualNum", i) for further using
output$indNum <- renderUI({
num <- as.integer(input$ninds)
req(num)
lapply(1:num, function(i) {
numericInput(paste0("individualNum", i), value = i, label = "", max = i, min = i)
})
})
# create reactive variable paste0("name", i) for further using
output$Name <- renderUI({
num <- as.integer(input$ninds)
req(num)
lapply(1:num, function(i) {
textInput(paste0("name", i), label = "")
})
})
# create reactie variable paste0("age", i) for further using
output$Age <- renderUI({
num <- as.integer(input$ninds)
req(num)
lapply(1:num, function(i) {
numericInput(paste0("age", i), label = "", value = 0)
})
})
}
# Run the app ----
shinyApp(ui, server)
截图-1
编辑:
我们可以将 isolate
与输入的当前值一起使用,并将其作为 numericInput
或 textInput
中的值参数传递。这将起作用,因为任何尚不存在的输入都会产生 NULL
.
server <- function(input, output) {
# create reactive variable paste0("individualNum", i) for further using
num <- reactive({
req(input$ninds)
input$ninds
})
output$indNum <- renderUI({
lapply(1:num(), function(i) {
numericInput(paste0("individualNum", i), value = i, label = "", max = i, min = i)
})
})
# create reactive variable paste0("name", i) for further using
output$Name <- renderUI({
lapply(1:num(), function(i) {
textInput(paste0("name", i), label = "", value = isolate(input[[paste0("name", i)]]))
})
})
# create reactie variable paste0("age", i) for further using
output$Age <- renderUI({
lapply(1:num(), function(i) {
numericInput(paste0("age", i), label = "", value = isolate(input[[paste0("age", i)]]))
})
})
}
原始答案使用 insertUI
:
我们可以实现这样的逻辑:创建一个计数器来存储当前输入的数字,制作两个按钮,一个用于添加,另一个用于删除输入。我们必须用具有唯一 ID 的 div 包装每个输入(因为输入函数通常会添加多个元素)。
library(shiny)
library(purrr)
ui <- fluidPage(
tabsetPanel(
tabPanel(
h4("Individual Information"),
fluidRow(
column(
4, fluidRow(
column(6, numericInput("ninds",
label = "Number of individuals",
value = 1, min = 1, step = 0.5, width = "300px"
)), column(3, actionButton("add_ui", "Add Individual", style = "background-color: green;")),
column(3, actionButton("remove_ui", "Remove Individual", style = "background-color: red;"))
)
)
),
br(),
fluidRow(
column(2, align = "center", strong("Individual #")),
column(5, align = "center", strong("Individual Name")),
column(5, align = "center", strong("Age"))
),
fluidRow(
column(2, wellPanel(id = "IndNumber")),
column(5, wellPanel(id = "Name")),
column(5, wellPanel(id = "Age"))
)
)
)
)
server <- function(input, output) {
# track the number of inputs
ui_counter <- reactiveVal(1)
observeEvent(input$add_ui, {
div_nms <- map_chr(c("individualNum", "name", "age"), ~ paste0("div", .x, ui_counter()))
# individual number
insertUI(
selector = "#IndNumber",
ui = div(
id = div_nms[[1]],
numericInput(paste0("individualNum", ui_counter()),
label = "",
value = ui_counter(),
min = ui_counter(),
max = ui_counter()
)
)
)
# name input
insertUI(
selector = "#Name",
ui = div(id = div_nms[[2]], textInput(paste0("name", ui_counter()),
label = ""
))
)
# age input
insertUI(
selector = "#Age",
ui = div(id = div_nms[[3]], numericInput(paste0("age", ui_counter()),
label = "",
value = 0
))
)
ui_counter(ui_counter() + 1)
})
# observer to remove the divs containing the inputs
observeEvent(input$remove_ui, {
if (ui_counter() > 1) {
walk(c("individualNum", "name", "age"), ~ removeUI(paste0("#div", .x, ui_counter() - 1)))
ui_counter(ui_counter() - 1)
}
})
}
# Run the app ----
shinyApp(ui, server)