Search code examples
ruser-interfaceshinytidyverseshinywidgets

How to use numericRangeInput() with isolate() when generating dynamic UI elements


I have been working with shiny for a little bit and I am trying my hand at generating dynamic UI elements to make data analysis easier for some people in my lab. So far I have had some moderate success in getting the basic functionality to work, but I am struggling with getting the numericRangeInput() function to work with dynamically generated UI the same way that textInput() can like in this example where the isolate() function is used to keep the same values in the input when updating the UI.

This approach could work in theory, but only when both values of the range are filled in. Outside of that it throws an error for minimum and maximum values since they are NA in the initialization which numericRangeInput() can not handle. A workaround would be to specify the values for the range in the server code before it gets sent to the UI output, but that just re-introduces the problem of resetting the user's values when adding or removing items from the list.

So far here is the minimum "working" example:

library(shiny)
library(shinyWidgets)
library(tidyverse)

ui <- fluidPage(
  actionButton("add",'+'),
  actionButton("sub",'-'),
  uiOutput("range"),
  textOutput("numbs")
)

server <- function(input, output, session) {
  num_col <- reactiveVal(2)
  
  col_numbs <- reactive(paste("#",seq_len(num_col())))
  
  observeEvent(input$add,{
    num_col(num_col()+1)
  })
  
  observeEvent(input$sub,{
    if(num_col()>1){
      num_col(num_col()-1)
    }
  })
  
  output$range <- renderUI({
    map(col_numbs(), ~ 
          numericRangeInput(.x, NULL,     
                            value = isolate(input[[.x]]), 
                            separator = "-", 
                            min = 0, 
                            step = 1)
    )
  })
  
  output$numbs <- renderText({
    map_chr(col_numbs(), ~ paste(input[[.x]],collapse = "-"))
  })
}

shinyApp(ui,server)

note that it produces the following errors in the console for every instance of numericRangeInput() being generated, and every subesquent generation so long as at least one of the values are not defined: Warning in min(value) : no non-missing arguments to min; returning Inf Warning in max(value) : no non-missing arguments to max; returning -Inf

changing the line value = isolate(input[[.x]]), to value = c(0,0) fixes this error at the cost of the ability to save the input values.

As always I appreciate any help that can be provided.


Solution

  • A very simple solution can be achieved by using the dplyr::coalesce function:

      numericRangeInput(.x, NULL,     
                        value = isolate(coalesce(input[[.x]], c(-Inf, Inf))), 
                        separator = "-", 
                        min = 0, 
                        step = 1)
    

    It returns the first non-missing value at each position. This works even if one/both sides of the input are empty.