I'm trying to create a data collection tool on R Shiny where the user can select as many categories as apply to them and then enter values for each. I've used uiOutput to allow the user to add a new category choice after clicking an action button.
For some reason, the numericInput that is created after clicking the action button will deselect after a split-second when the user is typing in a number, so it only catches one digit and you have to click it repeatedly to type in a full number.
I've tried changing the numericInput to a textInput and the same thing happens, so it's something to do with how I'm generating the uiOutput in the server, does it continually refresh and is there any way to stop it?
Example code given below, click on the new row button then try typing in the numericInput and you'll see. I have been stuck on this for ages and can't find any other questions similar so any help massively appreciated, thanks
library(tidyverse)
library(shiny)
library(shinyjs)
ui <- fluidPage(
fluidRow(wellPanel(h3("Category and quantity input"))),
wellPanel(fluidRow(column(width=4,selectInput("type0",label = h4("type"), choices= list("choice1" = 1,"choice2" = 2, "choice3"=3))),
column(width=4,numericInput("quantity0", label = h4("quantity"), value = 0, min=0)),
column(width=4,actionButton("New_row",label="Add new row"))),
uiOutput("new_row_added")
))
server <- function(input, output) {
ids <<- NULL
observeEvent(input$New_row,{
if (is.null(ids)){
ids <<- 1
}else{
ids <<- c(ids, max(ids)+1)
}
output$new_row_added <- renderUI({
tagList(
lapply(1:length(ids),function(i){
check_input_type <- paste0("type", ids[i])
check_input_quantity <- paste0("quantity", ids[i])
if(is.null(input[[check_input_type]])){
# Create a div that contains 3 new sub divs
div(fluidRow(column(width=4,
div(selectInput(paste0("type",ids[i]),label = "", choices= list("choice1" = 1,"choice2" = 2, "choice3"=3)))),
column(width=4,div(numericInput(paste0("quantity",ids[i]), label = "", value = 00, min=0))))
)
} else {
# Create a div that contains 3 existing sub divs
div(fluidRow(column(width=4,
div(selectInput(paste0("type",ids[i]),label = "", choices= list("choice1" = 1,"choice2" = 2, "choice3"=3), selected = input[[check_input_type]]))),
column(width=4,div(numericInput(paste0("quantity",ids[i]), label = "", min=0, value = input[[check_input_quantity]]))))
)
}
})
)
})
})
}
shinyApp(ui = ui, server = server)
You need to isolate input[[check_input_type]]. By doing isolate(input[[check_input_type]])
. If not, every time a new number is inserted inside that input, the ui will re render and cause the deselection.
App:
library(tidyverse)
library(shiny)
library(shinyjs)
ui <- fluidPage(
fluidRow(wellPanel(h3("Category and quantity input"))),
wellPanel(
fluidRow(
column(width = 4, selectInput("type0", label = h4("type"), choices = list("choice1" = 1, "choice2" = 2, "choice3" = 3))),
column(width = 4, numericInput("quantity0", label = h4("quantity"), value = 0, min = 0)),
column(width = 4, actionButton("New_row", label = "Add new row"))
),
uiOutput("new_row_added")
)
)
server <- function(input, output) {
ids <<- NULL
observeEvent(input$New_row, {
if (is.null(ids)) {
ids <<- 1
} else {
ids <<- c(ids, max(ids) + 1)
}
output$new_row_added <- renderUI({
tagList(
lapply(1:length(ids), function(i) {
check_input_type <- paste0("type", ids[i])
check_input_quantity <- paste0("quantity", ids[i])
if (is.null(isolate(input[[check_input_type]]))) {
# Create a div that contains 3 new sub divs
div(fluidRow(
column(
width = 4,
div(selectInput(paste0("type", ids[i]), label = "", choices = list("choice1" = 1, "choice2" = 2, "choice3" = 3)))
),
column(width = 4, div(numericInput(paste0("quantity", ids[i]), label = "", value = 00, min = 0)))
))
} else {
# Create a div that contains 3 existing sub divs
div(fluidRow(
column(
width = 4,
div(selectInput(paste0("type", ids[i]), label = "", choices = list("choice1" = 1, "choice2" = 2, "choice3" = 3), selected = isolate(input[[check_input_type]])))
),
column(width = 4, div(numericInput(paste0("quantity", ids[i]), label = "", min = 0, value = input[[check_input_quantity]])))
))
}
})
)
})
})
}
shinyApp(ui = ui, server = server)