Form input tags cannot have `id="nodeName"` or `id="nodeType"` inside `sidebarPanel()`
I am on Shiny 1.10.0
If I create a downloadButton in an app it appears enabled. If I create a selectInput before the downloadButton it becomes disabled at startup.
See reprex.
System details
Browser Version:
Microsoft Edge
Version 133.0.3065.82 (Official build) (64-bit)
Output of sessionInfo():
R version 4.4.0 (2024-04-24 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)
Matrix products: default
locale:
[1] LC_COLLATE=English_South Africa.utf8 LC_CTYPE=English_South Africa.utf8 LC_MONETARY=English_South Africa.utf8 LC_NUMERIC=C
[5] LC_TIME=English_South Africa.utf8
time zone: Africa/Johannesburg
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] shiny_1.10.0
loaded via a namespace (and not attached):
[1] jsonlite_1.8.9 compiler_4.4.0 promises_1.3.2 reprex_2.1.0 Rcpp_1.0.14 clipr_0.8.0 callr_3.7.6 later_1.4.1
[9] jquerylib_0.1.4 yaml_2.3.8 fastmap_1.2.0 mime_0.12 R6_2.5.1 knitr_1.47 tibble_3.2.1 R.cache_0.16.0
[17] pillar_1.9.0 bslib_0.9.0 R.utils_2.12.3 rlang_1.1.4 utf8_1.2.4 cachem_1.1.0 httpuv_1.6.15 xfun_0.45
[25] fs_1.6.5 sass_0.4.9 memoise_2.0.1 cli_3.6.3 withr_3.0.2 magrittr_2.0.3 ps_1.7.6 processx_3.8.4
[33] digest_0.6.36 rstudioapi_0.17.1 fontawesome_0.5.3 xtable_1.8-4 lifecycle_1.0.4 R.methodsS3_1.8.2 R.oo_1.26.0 vctrs_0.6.5
[41] evaluate_0.24.0 glue_1.7.0 styler_1.10.3 fansi_1.0.6 rmarkdown_2.27 purrr_1.0.2 pkgconfig_2.0.3 tools_4.4.0
[49] htmltools_0.5.8.1
Steps to reproduce the problem
library(shiny)
ui <- fluidPage(
selectInput("x", "X",
choices = c("A", "B")),
downloadButton(outputId = "y", label = "Y"),
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
Created on 2025-02-25 with reprex v2.1.0
In a relatively recent version of Shiny, we started disabling the download button until the download handler is registered on the server side. This protects you against a bunch of weirdness that can happen with the download button if you don't have a handler in place.
What's very likely is that there's an ID conflict in your app. Check your input/output IDs, or run your app with shiny::devmode(TRUE) enabled.
Here's a working version of your app:
library(shiny)
ui <- fluidPage(
selectInput("x", "X", choices = c("A", "B")),
downloadButton(outputId = "y", label = "Y"),
)
server <- function(input, output, session) {
output$y <- downloadHandler(
filename = function() {
"sample_data.csv"
},
content = function(file) {
# Create some sample data
data <- data.frame(
ID = 1:5,
Name = c("Alice", "Bob", "Charlie", "David", "Eve"),
Value = rnorm(5)
)
# Write the data to a CSV file
write.csv(data, file, row.names = FALSE)
}
)
}
shinyApp(ui, server)
Thank you for the assist - I was able to get to the bottom of this, kind of.
I did indeed leave out important context in my original post - I named the selectInput(inputId="nodeType") and put things in a sidebarLayout. After making those changes the downloadHandler is not registering for some reason so it remains disabled. There are references to "nodeType" in the codebase (in particular in selectize) but I found that renaming it to "nodeFlowType" did the trick for me. Not sure I need to pull this thread any more :)
Here is a minimal breaking example building on the last one:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("nodeType", "X", choices = c("A", "B")),
downloadButton(outputId = "y", label = "Y")
),
mainPanel("empty")
)
)
server <- function(input, output, session) {
output$y <- downloadHandler(
filename = function() {
"sample_data.csv"
},
content = function(file) {
# Create some sample data
data <- data.frame(
ID = 1:5,
Name = c("Alice", "Bob", "Charlie", "David", "Eve"),
Value = rnorm(5)
)
# Write the data to a CSV file
write.csv(data, file, row.names = FALSE)
}
)
}
shinyApp(ui, server)
Actually, I wonder if this is related to downloadButton being in sidebarPanel, which should contain input controls only?
Actually, I wonder if this is related to downloadButton being in sidebarPanel, which should contain input controls only?
It's much much more likely that there's a conflict in your IDs. Can you go back to the state when the app wasn't working? If so, with the app open in the browser, if you open the browser's developer tools (by right-clicking anywhere on the page and selecting "Inspect" or "Inspect element"), the Console tab should have warnings or error messages pointing to duplicate ID problems.
Thanks - there are no warnings about duplicates (I tried refresh also).
The above was a pretty compact breaking example. If you remove the sidebarLayout/sidebarPanel/mainPanel the button won't be disabled and if you replace "nodeType" with something else it also won't be disabled. So I think internally "nodeType" is being used somewhere. Also just to note the js console gives the same output when I "fix" the minimal example in the above ways.
The above was a pretty compact breaking example. If you remove the sidebarLayout/sidebarPanel/mainPanel the button won't be disabled and if you replace "nodeType" with something else it also won't be disabled. So I think internally
"nodeType"is being used somewhere.
Wow. Sorry, I hadn't run the example yet, but you're right! That's... bizarre. Here's the smallest reprex I can make, which comes down to having a <select id="nodeType"> tag inside a <form> tag with the download button:
library(shiny)
ui <- fluidPage(
tags$form(
tags$select(id="nodeType"),
downloadButton(outputId = "y", label = "Y"),
)
)
server <- function(input, output, session) {
output$y <- downloadHandler(
filename = function() "sample.txt",
content = function(file) {
writeLines(Sys.time(), file)
}
)
}
shinyApp(ui, server)
The <form> tag comes into play because that's what sidebarPanel() uses, but if you use bslib::page_sidebar() you avoid this problem entirely.
ui <- bslib::page_sidebar(
sidebar = sidebar(
tags$select(id="nodeType"),
downloadButton(outputId = "y", label = "Y"),
)
)
I'm amazed that the reprex doesn't throw any console errors, but I can get a console error by changing id = "nodeType" to id = "nodeName". Both are element properties and it seems that something is causing jQuery to try to read the nodeName or nodeType property when the select input with that name appears in a <form>...
...actually even this causes an console error:
ui <- fluidPage(
tags$form(
tags$select(id="nodeName")
)
)
> e.nodeName.toLowerCase is not a function
I'm going to re-open the issue because this is clearly not expected!
Seems like this is also an old and still open issue in React: https://github.com/search?q=repo%3Afacebook%2Freact+%22nodeName.toLowerCase%22&ref=opensearch&type=issues
Also any form input elements with the id="nodeType" is enough:
-
tags$select(id = "nodeType") -
tags$input(id = "nodeType") -
tags$button(id = "nodeType"), etc...
Yuck (see method 3 in the question)
WOW, this is the safe way to read e.nodeName/e.nodeType:
Object.getOwnPropertyDescriptor(Node.prototype, "nodeName").get.call(e)
and even then it's breaking encapsulation pretty hard!