问题
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