shiny
shiny copied to clipboard
selectInput() turns NA into "NA"
library(shiny)
ui <- fluidPage(
selectInput("x", "x", choices = c("a", "b", NA)),
textOutput("out")
)
server <- function(input, output, session) {
output$out <- renderPrint(str(input$x))
}
shinyApp(ui, server)
It also turns numerics, or anything else, to character.
I personally think this is fine, just needs to be documented that the return type is always a string. If you want the user to be able to select a special value, usually what I do is use a named vector of choices and check for it, eg
NA_CHOICE <- "..na.."
choices = c("a", "b", "None" = NA_CHOICE)
...
if (input$x == NA_CHOICE) { ... }
I think the bigger issue here is the lack of documentation and consistency in what the return type is (which is an open and likely forgotten issue)
Converting other types to a string is fine since this is fundamentally a string based input. Converting NA to "NA" is not because it is changing the meaning within a character vector.
I'm already quite aware how to work around the problem, but I think it should be fixed in shiny.
I have 0 doubt you know how to work around almost any issue in R, but for the average useR who doesn't have your skillset I wanted to add the workaround so that when others come to this thread they can see it :)
Agreed it would be nice to fully support character vectors, I'm not sure how feasible this is to fix though. Just because these values are really stored client-side and get communicated back to shiny, I don't know if there's an equivalent to "missing string value" in javascript. Hopefully the shiny team proves me wrong!
I think one of the complexity here is that JavaScript has no concept of NA per se, and that the conversion is currently done through {jsonlite}, which turns NA in "NA" 🤔
So one way to do it would be to transform NA into a specific string that is very unlikely to be used as an input (for example ".shiny_NA_.") before sending it to the UI, and then do a check when it comes back and turn it into NA back.
This idea of trying to find a value that isn't naturally going to occur in JS -> shiny is useful for this PR as well: https://github.com/rstudio/shiny/pull/2770
For people needing an immediate solution here's an ugly hack I'm doing to account for this issue:
includes_na <- reactive({
sum('NA' == input$select_inupt_ele)
})
select_input_react <- reactive({
if (includes_na() == 1) {
c(input$select_inupt_ele, NA)
} else {
input$select_inupt_ele
}
})
This basically just appends an NA to the choices of your selectInput element. You can then leverage the list calling select_input_react().
Hi! I was going through Mastering Shiny and noticed that renderTable changes "NA" to NA:
ui <- fluidPage(
tableOutput("tab")
)
server <- function(input, output, session) {
output$tab <- renderTable(data.frame(x=c("NA", "Japan")))
}
shinyApp(ui, server)
The first row of the generated table has the NA class an shows up in light gray.
So one way to do it would be to transform
NAinto a specific string that is very unlikely to be used as an input (for example".shiny_NA_.") before sending it to the UI, and then do a check when it comes back and turn it intoNAback.
To avoid any possibility of a user inputting the same string chosen to represent NAs, it might be simpler to recode choices (e.g., to 1:length(choices), and then invert the recoding when the input comes back. I'm up for implementing this, but I'm not sure where the "selections come back from js" part of the code is.
I wasn't sure how to pass a remapping of choices through from the selectInput() call to an input handler, so I attempted to implement @ColinFay's recommendation instead.
I experienced the same issue when I used ISO-3166 Alpha-2 of different countries. It turns out that a country like Namibia has an alpha-2 code of "NA" which also reads as NA.
As a workaround, I decided to make it "NAM"