javascriptrshinydt

How to change the values of a DT's filtering sliders in R Shiny with JavaScript?


I'd like to modify the values of the filtering sliders produced by DT in JavaScript. I want to change these values rather than filter the data elsewhere as, ultimately, I want to add presets so you can quickly and easily update the sliders with specific ranges and they will be displayed right above the sliders.

Using runjs() I've successfully selected the slider's elements as follows:

slider = document.querySelectorAll('.noUi-target')[0];

However, my attempts to set or modify the values of the slider have not been successful. The slider seems to be some form of a noUiSlider, but the examples I've seen of setting such a slider's values have not worked here. My attempts include the following, the first of which worked for a shinyWidgets::noUiSliderInput():

slider.noUiSlider.set([10.4, 20])
slider.noUiSlider('options').set([10.4, 20])
slider.setValue([10.4, 20])

I wondered if I needed to import the noUiSlider JS library somehow, or if the slider isn't rendered as a noUiSlider or slider at all? Here's a sample app that lays out the basic structure of what I want to do: When the filterOutHighMPG button is clicked, I want the values of the filtering slider beneath mpg to be set to 10.4 to 20 and the data filtered accordingly.

library(shiny)
library(DT)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  actionButton('filterOutHighMPG', 'Filter Out High MPG'),
  DTOutput('table')
)

server <- function(input, output, session) {
  
  output$table <- renderDT({
    datatable(mtcars, filter = 'top')
  })
  
  observeEvent(input$filterOutHighMPG, {
    # Set mpg filter to [10.4, 20]
    runjs("")
  })
  
}

shinyApp(ui, server)

Solution

  • Your selection of slider using document.querySelectorAll('.noUi-target')[0]; is suitable, and then you can use

    $(slider).noUiSlider({
        start: new_vals,
        range: {
            "min": old_lims[0],
            "max": old_lims[1]
        }
    }, true);
    

    where new_vals is determined by your desired values 10.4 and 20 (the filter range) and old_lims are the old limits of the slider, but these can of course also be updated. See the below minimal example for how you can obtain them. Also note that you have to multiply these values with the current scale of the slider.

    enter image description here

    Below I used sendCustomMessage but it is of course similarly also possible using shinyjs.

    library(shiny)
    library(DT)
    
    ui <- fluidPage(
      tags$head(
        tags$script(
          'Shiny.addCustomMessageHandler("updateMpgSlider",
            function(message) {
              var slider = document.querySelectorAll(".noUi-target")[0];
              
              // determine scale and calculate new values
              var scale = Math.pow(10, Math.max(0, +$(slider).data("scale") || 0));
              var new_vals = [message.start0 * scale, message.start1 * scale];
              
              // determine current limits
              var old_lims = $(slider).noUiSlider("options").range;
              old_lims = [old_lims.min, old_lims.max];
        
              // set slider using new_vals and old_lims
              $(slider).noUiSlider({
                  start: new_vals,
                  range: {
                      "min": old_lims[0],
                      "max": old_lims[1]
                  }
              }, true);
          });'
        )
      ),
      actionButton('filterOutHighMPG', 'Filter Out High MPG'),
      DTOutput('table')
    )
    
    server <- function(input, output, session) {
      
      output$table <- renderDT({
        datatable(mtcars, filter = 'top', callback = JS(callback))
      })
      
      observeEvent(input$filterOutHighMPG, {
        # Set mpg filter to [10.4, 20]
        session$sendCustomMessage(type = 'updateMpgSlider',
                                  message = list(start0 = 10.4, start1 = 20))
       })
    }
    
    shinyApp(ui, server)