在 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 问题 中,我有一个 noUiSliderInput
在 javascript
的帮助下将其标签更新为科学记数法格式。
剩下的问题是当输入为 0 时 noUiSliderInput
跳转到 10,因此它应该跳转到 1。(10^0 是 1,而不是 10)。我怀疑 Math.pow(10,x) 的问题是原因,但我不确定
更新:
滑块跳到最大值的初始问题已通过更改 javascript
代码得到解决,该代码现在将 shiny
中的值作为滑块的最小值、最大值和值的输入。
我已经在问题中更改了应用程序的代码以表示当前情况。
#更新结束#
这里使用第二个 sliderInput
和 numericInput
框来模拟行为,这会导致 noUiSliderInput 所需的所有三个值都变为 cahange
在我的真实 app
中,noUiSliderInput
链接到 plot
,用户选择 plot
的任何数据 column
。因此,无论用户选择绘制什么数据,都需要 noUiSliderInput
到 update
,即 range
。 slider
还链接到具有较早创建的设置的 datatable
(数据中某些列的阈值数据 table),并且当用户单击 datatable
条目,它将 update
下拉列表显示 select 要绘制哪些数据(因此 noUiSliderInput
range
),以及在什么高度threshold (noUiSliderInput
value
) 应基于 datatable
中的值。
这会同时触发三个变化,范围(即 noUiSliderInput
的最小值和最大值),以及 noUiSliderInput
的值。
我设法得到一个模拟此行为的虚拟 app
,除了 noUiSliderInput
的 value
似乎被当前的 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)
在较早的 SO 问题 noUiSliderInput
在 javascript
的帮助下将其标签更新为科学记数法格式。
剩下的问题是当输入为 0 时 noUiSliderInput
跳转到 10,因此它应该跳转到 1。(10^0 是 1,而不是 10)。我怀疑 Math.pow(10,x) 的问题是原因,但我不确定
更新:
滑块跳到最大值的初始问题已通过更改 javascript
代码得到解决,该代码现在将 shiny
中的值作为滑块的最小值、最大值和值的输入。
我已经在问题中更改了应用程序的代码以表示当前情况。
#更新结束#
这里使用第二个 sliderInput
和 numericInput
框来模拟行为,这会导致 noUiSliderInput 所需的所有三个值都变为 cahange
在我的真实 app
中,noUiSliderInput
链接到 plot
,用户选择 plot
的任何数据 column
。因此,无论用户选择绘制什么数据,都需要 noUiSliderInput
到 update
,即 range
。 slider
还链接到具有较早创建的设置的 datatable
(数据中某些列的阈值数据 table),并且当用户单击 datatable
条目,它将 update
下拉列表显示 select 要绘制哪些数据(因此 noUiSliderInput
range
),以及在什么高度threshold (noUiSliderInput
value
) 应基于 datatable
中的值。
这会同时触发三个变化,范围(即 noUiSliderInput
的最小值和最大值),以及 noUiSliderInput
的值。
我设法得到一个模拟此行为的虚拟 app
,除了 noUiSliderInput
的 value
似乎被当前的 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)