Using javascript to alter noUIslider tags in R Shiny error when performing Math.pow(10,x) when x = 0

白昼怎懂夜的黑 提交于 2019-12-24 14:39:43

问题


In an earlier SO question here I have come to a point where I have a noUiSliderInput that gets it's labels updated to a scientific notation format with the help of javascript.

The remaining problem is that the noUiSliderInput jumps to 10 when the input is 0 and it therefore should jump to 1. (10^0 is 1, not 10). I suspect a problem with Math.pow(10,x) is the cause but I am not sure

UPDATE: The initial problem of the slider jumping to the maximum value has been fixed by changing the javascript code, which now takes values from shiny as input for the min, max and value of the slider. I have changed the code of the app here in the question to represent the current situation.

#END UPDATE#

The behavior is simulated here with the use of a second sliderInput and numericInput boxes that causes all three values needed for the noUiSliderInput to cahange

In my real app the noUiSliderInput is linked to a plot, and whatever data column the user chooses to plot. So, whatever data the user chooses to plot will require the noUiSliderInput to update it's range. The slider is also linked to a datatable with earlier created settings (a datatable of threshold values for some of the columns in the data), and when the user clicks a datatable entry, it will update the drop down that selects which data is to be plotted (and thus the noUiSliderInput range), as well as at what height the threshold (noUiSliderInput value) should be based on the value in the datatable.

This fires three changes at once, range (i.e. min & max of noUiSliderInput), as well as value of the noUiSliderInput.

I managed to get a dummy app that simulates this behavior working, except the value of the noUiSliderInput gets updated wrong by the current javascript it seems.

The schematic looks like this roughly

Where you can see that clicking on the table will send a value to the noUiSliderInput value, and a parameter name to the parameter selectInput, which then gets the data, sends it to the plot, and send the min, max: range to the noUiSliderInput. It is build this way because the user can also select parameters (columns) from the selectInput directly (many of which are not in the table of thresholds)

App so far:

library(shiny)
library(shinyWidgets)
library(shinyjs)

js <- function(Min, Max, Start){
  sprintf(paste(
    "var slider = document.getElementById('Histoslider').noUiSlider;",
    "slider.updateOptions({",
    "  start: %s,",
    "  range: {min: %s, max: %s},",
    "  format: wNumb({", 
    "    encoder: function(x){return parseFloat(Math.pow(10,x));}", 
    "  })", 
    "});",
    "var pipsValues = $('.noUi-value');",
    "pipsValues.each(function(){$(this).html('10<sup>'+$(this).attr('data-value')+'</sup>')});",
    sep = "\n"
  ), Start, Min, Max)
}

ui <- fluidPage(
  useShinyjs(),
  tags$br(),
  fluidRow(
    column(4,
           uiOutput('TestSliderOut'), 
           actionButton(inputId = "UpdateSlider", label = 'Update')
    ),
    column(6, 
           sliderInput("slider2", label = NULL, min = -5, max = 20, value= c(1, 5), step = 1),
           numericInput(inputId = 'SlMin', label = 'Min', value = 1, min = -5, max = 20),
           numericInput(inputId = 'SlMax', label = 'Max', value = 5, min = -5, max = 20),
           numericInput(inputId = 'SlVal', label = 'Val', value = 3, min = -5, max = 20),
           br(),
           h5('Update counter'),
           verbatimTextOutput(outputId = "updates"),
           h5('slider value'),
           verbatimTextOutput(outputId = 'ouputval')
    )
  )
)



server <- function(input, output, session) {

  values <- reactiveValues( Counter = 0)

  output$TestSliderOut <- renderUI({
    noUiSliderInput(
      inputId = "Histoslider", label = "Slider vertical:",
      min = -2, max = 4, step = 0.01,
      value = 0, margin = 100, 
      pips = list(mode="range", density=2),
      orientation = "vertical", 
      width = "300px", height = "300px",   direction = "rtl", 
      behaviour = "tap"
    )
  })

  observeEvent(input$slider2, {
    ## simulates the change of multipe parameters of the slider: min, max, value
    newvalue <- (input$slider2[2]-input$slider2[1])/2+input$slider2[1]
    updateNumericInput(session, inputId = 'SlMin', value = input$slider2[1])
    updateNumericInput(session, inputId = 'SlMax', value = input$slider2[2])
    updateNumericInput(session, inputId = 'SlVal', value = newvalue)

  })

  observe({
    updateNoUiSliderInput(session, inputId = 'Histoslider', range = c(input$SlMin, input$SlMax), value = input$SlVal)
    shinyjs::delay(1, {  ## delay the javascript so that it runs after the slider has updated its values
      runjs(js(input$SlMin, input$SlMax, input$SlVal))     })
  })

  output$updates <- renderPrint(values$Counter)


  output$ouputval <- renderPrint(input$Histoslider)

  observeEvent(input$SlMin, { values$nouiMin <- input$SlMin })
  observeEvent(input$SlMax, { values$nouiMax <- input$SlMax })
  observeEvent(input$SlVal, { values$nouiVal <- input$SlVal })

  observeEvent(input$Histoslider, {
    print('changing code')
    runjs(js(values$nouiMin, values$nouiMax, values$nouiVal))     ## fires ones on first build of the slider
  }, once = TRUE)

}

shinyApp(ui, server)

回答1:


Hmm that does look strange. It seems to work by using the set method (set the value of the slider) instead of updating the start option:

js <- function(Min, Max, Start){
  sprintf(paste(
    "var slider = document.getElementById('Histoslider').noUiSlider;",
    "slider.updateOptions({",
    "  range: {min: %s, max: %s},",
    "  format: wNumb({", 
    "    decimals: 2,",
    "    encoder: function(x){return parseFloat(Math.pow(10,x));}", 
    "  })", 
    "});",
    "slider.set(%s);",
    "var pipsValues = $('.noUi-value');",
    "pipsValues.each(function(){$(this).html('10<sup>'+$(this).attr('data-value')+'</sup>')});",
    sep = "\n"
  ), Min, Max, Start)
}

Note that the updateNoUiSliderInput in your code is useless, because the slider is updated by the JS code. So replace

  observe({
    updateNoUiSliderInput(session, inputId = 'Histoslider', range = c(input$SlMin, input$SlMax), value = input$SlVal)
    shinyjs::delay(1, {  ## delay the javascript so that it runs after the slider has updated its values
      runjs(js(input$SlMin, input$SlMax, input$SlVal))     })
  })

with

  observeEvent(list(input$SlMin, input$SlMax, input$SlVal), {
      runjs(js(input$SlMin, input$SlMax, input$SlVal))     
  }, ignoreInit = TRUE)


来源:https://stackoverflow.com/questions/54618717/using-javascript-to-alter-nouislider-tags-in-r-shiny-error-when-performing-math

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!