shiny icon indicating copy to clipboard operation
shiny copied to clipboard

Reactive selectizeInput box fail to update correctly in Shiny 1.8.0, first value of previous allowed choices are kept

Open Roleren opened this issue 2 years ago • 2 comments

System details

Browser Version:

Output of sessionInfo():

R version 4.3.0 (2023-04-21)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 22.04.3 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=nb_NO.UTF-8        LC_COLLATE=en_US.UTF-8     LC_MONETARY=nb_NO.UTF-8   
 [6] LC_MESSAGES=en_US.UTF-8    LC_PAPER=nb_NO.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=nb_NO.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Oslo
tzcode source: system (glibc)

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] DT_0.30                     shiny_1.8.0                 fstcore_0.9.14              data.table_1.14.8          
 [5] RiboCrypt_1.5.1             ORFik_1.23.5                GenomicAlignments_1.36.0    Rsamtools_2.16.0           
 [9] Biostrings_2.69.2           XVector_0.40.0              SummarizedExperiment_1.30.1 Biobase_2.60.0             
[13] MatrixGenerics_1.12.3       matrixStats_1.2.0           GenomicRanges_1.52.0        GenomeInfoDb_1.36.1        
[17] IRanges_2.34.1              S4Vectors_0.38.1            BiocGenerics_0.46.0        

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3      shape_1.4.6             rstudioapi_0.14         jsonlite_1.8.4          magrittr_2.0.3         
  [6] shinyjqui_0.4.1         GenomicFeatures_1.52.0  rmarkdown_2.21          fs_1.6.2                GlobalOptions_0.1.2    
 [11] BiocIO_1.10.0           zlibbioc_1.46.0         vctrs_0.6.2             NGLVieweR_1.3.1         memoise_2.0.1          
 [16] RCurl_1.98-1.12         usethis_2.1.6           htmltools_0.5.5         S4Arrays_1.2.0          progress_1.2.2         
 [21] curl_5.0.0              sass_0.4.6              bslib_0.5.1             fontawesome_0.5.1       htmlwidgets_1.6.2      
 [26] plotly_4.10.1           cachem_1.0.8            mime_0.12               lifecycle_1.0.3         iterators_1.0.14       
 [31] pkgconfig_2.0.3         Matrix_1.6-4            R6_2.5.1                fastmap_1.1.1           clue_0.3-64            
 [36] GenomeInfoDbData_1.2.10 digest_0.6.31           colorspace_2.1-0        ps_1.7.5                shinycssloaders_1.0.0  
 [41] AnnotationDbi_1.62.1    DESeq2_1.40.1           pkgload_1.3.2           crosstalk_1.2.0         RSQLite_2.3.1          
 [46] filelock_1.0.2          fansi_1.0.4             httr_1.4.5              abind_1.4-5             compiler_4.3.0         
 [51] remotes_2.4.2           bit64_4.0.5             doParallel_1.0.17       BiocParallel_1.34.0     DBI_1.1.3              
 [56] pkgbuild_1.4.0          R.utils_2.12.2          biomaRt_2.56.0          sessioninfo_1.2.2       rappdirs_0.3.3         
 [61] DelayedArray_0.26.1     rjson_0.2.21            tools_4.3.0             httpuv_1.6.9            fst_0.9.8              
 [66] R.oo_1.25.0             glue_1.6.2              callr_3.7.3             restfulr_0.0.15         promises_1.2.0.1       
 [71] grid_4.3.0              cluster_2.1.4           generics_0.1.3          gtable_0.3.3            BSgenome_1.68.0        
 [76] R.methodsS3_1.8.2       tidyr_1.3.0             hms_1.1.3               xml2_1.3.4              utf8_1.2.3             
 [81] markdown_1.6            foreach_1.5.2           pillar_1.9.0            stringr_1.5.0           later_1.3.1            
 [86] circlize_0.4.15         dplyr_1.1.2             BiocFileCache_2.8.0     lattice_0.21-8          rtracklayer_1.60.0     
 [91] bit_4.0.5               tidyselect_1.2.0        ComplexHeatmap_2.16.0   locfit_1.5-9.7          miniUI_0.1.1.1         
 [96] knitr_1.42              gridExtra_2.3           xfun_0.39               devtools_2.4.5          stringi_1.7.12         
[101] lazyeval_0.2.2          yaml_2.3.7              evaluate_0.20           codetools_0.2-19        tibble_3.2.1           
[106] cli_3.6.1               xtable_1.8-4            processx_3.8.1          munsell_0.5.0           jquerylib_0.1.4        
[111] biomartr_1.0.8.9000     Rcpp_1.0.10             dbplyr_2.3.2            png_0.1-8               XML_3.99-0.14          
[116] parallel_4.3.0          ellipsis_0.3.2          shinyhelper_0.3.2       ggplot2_3.4.2           rclipboard_0.2.0       
[121] blob_1.2.4              prettyunits_1.1.1       profvis_0.3.8           urlchecker_1.0.1        bitops_1.0-7           
[126] viridisLite_0.4.2       scales_1.2.1            purrr_1.0.1             crayon_1.5.2            GetoptLong_1.0.5       
[131] rlang_1.1.1             cowplot_1.1.1           KEGGREST_1.40.0         shinyjs_2.1.0          

Example application or steps to reproduce the problem

library(shiny)
ui <- fluidPage(
  titlePanel(tag("u", "Test"), "Test"),
  selectizeInput(
    inputId = "selectID",
    label = "Select id type",
    choices = c("Numeric", "Letters"),
    selected = "Numeric",
    multiple = FALSE
  ),
  selectizeInput(
    inputId = "library",
    label = "Select libraries",
    choices = as.character(seq(5)),
    selected = 1,
    multiple = TRUE
  ),
  h3("And now!?"),
  br(),
)
server <- function(input, output, session) {

  observeEvent(input$selectID, {
    if (input$selectID == "Numeric") {
      choices <- as.character(seq(5))
    } else {
      choices <- LETTERS[1:5]
    }
    updateSelectizeInput(
      inputId = "library",
      choices = choices,
      selected = choices[1],
      server = TRUE
    )

  },
               ignoreNULL = TRUE, ignoreInit = FALSE)
  session$onSessionEnded(function() { stopApp() })
}
print(shinyApp(ui, server, options = list(launch.browser = TRUE)))

Describe the problem in detail

A new bug in 1.8.0, I can verify that this does not happen in 1.7.4, which I had before I updated today.

When you have a box (library) with selectable numbers 1,2,3,4,5 and another box (selectID) that defines id type of box (library) (numeric is default or character: i.e. changes the selection in (library) to A,B,C,D,E. The total allowed selection of (library) will not be A,B,C,D,E, but 1,A,B,C,D,E. If you flip back to numeric, the allowed options are then: A,1,2,3,4,5

Even though you restrict the types on updateSelectizeInput, this is clearly ignored as the first value of previous allowed choices are kept

This breaks our app on www.Ribocrypt.org

Roleren avatar Jan 09 '24 13:01 Roleren

Hi @Roleren, thanks for the bug report!

One important detail: this only happens when server = TRUE. In this case, the full set of choices are kept back on the server because they could be potentially large and are only sent to the client app when requested. If you completely change the available choices, the client won't know that the current (about to be old) values are no longer valid, so it keeps those values as options.

We can do slightly better than the current situation when selected is provided. I've just added a bit of code in #3967 that will clear the current value of the select input before performing the choices update when selected is provided.

Here are two other options to get around this issue with released shiny:

  1. Use fully client-side selectize inputs, i.e. server = FALSE.

  2. Manually clear the selection before updating the choices and current value, but having an observeEvent() that first clears the selection:

    choices <- reactive({
      if (input$selectID == "Numeric") {
        as.character(seq(5))
      } else {
        LETTERS[1:5]
      }
    })
    
    observeEvent(choices(), {
      # Clear the selection before updating choices
      updateSelectizeInput(
        inputId = "library",
        selected = NULL,
        server = TRUE
      )
    })
    
    observeEvent(choices(), {
      updateSelectizeInput(
        inputId = "library",
        choices = choices(),
        selected = choices()[1],
        server = TRUE
      )
    })
    

gadenbuie avatar Jan 09 '24 15:01 gadenbuie

Thanks, this should solve the issue for now. As far as I understand, the old value is still kept if selected is not specified ?

Roleren avatar Jan 09 '24 17:01 Roleren

I'm not sure if #3967 fixed this. I'm still having the same issue as explained in the original post with shiny v1.8.1.1 and development version.

trangdata avatar Jul 09 '24 17:07 trangdata

@trangdata I just tried the reprex in the original post with shiny v1.8.1.1 and it worked for me as expected after #3967. If you're encountering a similar issue it might actually be a new or different problem. Can you please open a new issue with a reproducible example so we can take a look?

gadenbuie avatar Jul 09 '24 18:07 gadenbuie

I ran the same reprex with shiny v1.8.1.1. Maybe I can clarify what I see a little bit:

When I first load the app, I have Numeric. I select libraries 1, 3, 5, then switch to Letters. Now, the selected library is A. However, when I click on the input box to select more libraries (expectedly only letters), I see 1, 3, 5 as options also.

Maybe I'm missing something obvious?!

trangdata avatar Jul 10 '24 00:07 trangdata

I don't think it's that you missed something obvious, I had to re-read the issue again carefully to remember...

When I first load the app, I have Numeric. I select libraries 1, 3, 5, then switch to Letters. Now, the selected library is A. However, when I click on the input box to select more libraries (expectedly only letters), I see 1, 3, 5 as options also.

This part of the issue isn't fixed by #3967, because

when server = TRUE ... the full set of choices are kept back on the server because they could be potentially large and are only sent to the client app when requested. If you completely change the available choices, the client won't know that the current (about to be old) values are no longer valid, so it keeps those values as options.

My recommendation for solving this part of the problem was to

Manually clear the selection before updating the choices and current value, but having an observeEvent() that first clears the selection

by adding the following to the server logic above the observeEvent() that sets the new selection and choices.

observeEvent(choices(), {
  # Clear the selection before updating choices
  updateSelectizeInput(
    inputId = "library",
    selected = NULL,
    server = TRUE
  )
})

gadenbuie avatar Jul 10 '24 13:07 gadenbuie

Ah okay! Thank you for clarifying Garrick! I saw your workaround in the earlier comment but thought that #3967 fixed everything and I would not have to manually clear the selection before updating the choices. But it makes sense now. Thanks!

trangdata avatar Jul 10 '24 14:07 trangdata