在 x = 0 时执行 Math.pow(10,x) 时,使用 javascript 更改 R Shiny 错误中的 noUIslider 标签

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

在较早的 SO 问题 中,我有一个 noUiSliderInputjavascript 的帮助下将其标签更新为科学记数法格式。

剩下的问题是当输入为 0 时 noUiSliderInput 跳转到 10,因此它应该跳转到 1。(10^0 是 1,而不是 10)。我怀疑 Math.pow(10,x) 的问题是原因,但我不确定

更新: 滑块跳到最大值的初始问题已通过更改 javascript 代码得到解决,该代码现在将 shiny 中的值作为滑块的最小值、最大值和值的输入。 我已经在问题中更改了应用程序的代码以表示当前情况。

#更新结束#

这里使用第二个 sliderInputnumericInput 框来模拟行为,这会导致 noUiSliderInput 所需的所有三个值都变为 cahange

在我的真实 app 中,noUiSliderInput 链接到 plot,用户选择 plot 的任何数据 column。因此,无论用户选择绘制什么数据,都需要 noUiSliderInputupdate,即 rangeslider 还链接到具有较早创建的设置的 datatable(数据中某些列的阈值数据 table),并且当用户单击 datatable 条目,它将 update 下拉列表显示 select 要绘制哪些数据(因此 noUiSliderInput range),以及在什么高度threshold (noUiSliderInput value) 应基于 datatable 中的值。

这会同时触发三个变化,范围(即 noUiSliderInput 的最小值和最大值),以及 noUiSliderInput 的值。

我设法得到一个模拟此行为的虚拟 app,除了 noUiSliderInputvalue 似乎被当前的 javascript 更新错误。

原理图大概是这样的

在那里你可以看到点击table会将一个值发送到noUiSliderInput值,并向参数selectInput发送一个参数名称,然后获取数据,将它发送到 plot,并将最小值,最大值:range 发送到 noUiSliderInput。 它是这样构建的,因为用户也可以直接从selectInput中select参数(columns)(其中许多不在table的阈值中)

到目前为止的应用程序:

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)

嗯,这看起来确实很奇怪。它似乎通过使用 set 方法(设置滑块的值)而不是更新 start 选项来工作:

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)
}

请注意,您代码中的updateNoUiSliderInput是无用的,因为滑块是由JS代码更新的。所以替换

  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))     })
  })

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