#r #shiny #reactive
#r #блестящий #реактивный
Вопрос:
Вот простое приложение, в котором пользователь может либо ввести (numericInput, bpm_numeric), либо сдвинуть (sliderInput, bpm_slider) вводимое значение. Оба этих элемента пользовательского интерфейса должны оставаться синхронизированными с любым выбором. Однако выходные данные необходимо отслеживать только один раз. Ниже я отслеживаю его дважды, но мне нужна только одна копия «выбора / ввода» от пользователя. Я думаю, что мне нужно использовать reactiveValues
, но я не уверен в подходе.
library(shiny)
ui <- fluidPage(
titlePanel("Reverb amp; Delay Calculator"),
sidebarLayout(
sidebarPanel(
uiOutput("bpm_numeric"),
uiOutput("bpm_slider")
),
mainPanel(
p("Numeric value is:"),
verbatimTextOutput("numeric_val"),
p("Slider value is:"),
verbatimTextOutput("slider_val")
)
)
)
server <- function(input, output, session) {
output$bpm_numeric = renderUI({
numericInput(
"bpm_numeric",
"BPM Numeric",
value = 60, min = 40, max = 300
)
})
output$bpm_slider = renderUI({
sliderInput(
"bpm_slider",
"BPM Slider",
value = 60, min = 40, max = 300
)
})
# See how I can track "both", but really I only need to track "one" since
# they should stay in-sync with one another. Maybe reactiveValues???
output$numeric_val <- renderText({ input$bpm_numeric })
output$slider_val <- renderText({ input$bpm_slider })
observeEvent(input$bpm_numeric, {
val = input$bpm_numeric
updateSliderInput(session, "bpm_slider", value = val)
})
observeEvent(input$bpm_slider, {
val = input$bpm_slider
updateNumericInput(session, "bpm_numeric", value = val)
})
}
shinyApp(ui = ui, server = server)
ПРИМЕЧАНИЕ: В настоящее время я использую uiOuput()
renderUI()
update___Input()
, это не обязательно требование. Я открыт для других подходов при условии, что пользовательский интерфейс ввода остается синхронизированным, у меня есть одна копия синхронизированного вывода.
Комментарии:
1. Если вам нужно ответить только на один из них, почему у вас есть два
renderText
блока?observeEvent
Блоки гарантируют, что они остаются синхронизированными, оттуда все, что нуждается в этом значении, должно иметь возможность зависеть только от одного из них.2. почему бы вам просто не удалить один из
renderText
выходных данных?3. @r2evans — Полагаю, я хочу обобщить, а не просто «выбрать» одно из двух? Я согласен, что выбор произвольный, но я хотел бы думать, что есть какой-то метод абстрагирования от этого, а не привязки к одному конкретному выбору?
4. Возможно, я что-то упускаю из того, что вы говорите, но для меня общим решением является именно «зависеть только от одного из них». В SQL у вас есть
coalesce
, который выдает вам только первый ненулевой из его аргументов; похоже, вы хотите что-то похожее на это здесь. Честно говоря, я не нахожу в этом никакой пользы, поскольку: (1) я знаю идентификатор всех из них, поэтому могу выбрать один, и (2) Я контролирую актуальность всех кандидатов. В более сложном примере попытка условно зависеть от более чем одного может легко привести к повторным зависимостям, вызывая в лучшем случае мерцание, возможно, ненужные вычисления.5. Вы пытаетесь обобщить, «как использовать это число», или как убедиться, что «2 или более элементов пользовательского интерфейса синхронизированы с одним и тем же значением»?
Ответ №1:
Вот мысль. Он способен немного обобщать, но все равно зависит от того, какие идентификаторы вы хотите объединить таким образом.
Изменения к вашему текущему примеру:
- Определите глобальное
defbmp <- 60
, полезно, чтобы убедиться, что все элементы начинаются с одного и того же места. - За пределами нового
useval
реактивного замените все ссылки наinput$bpm_numeric
или_slider
наuseval()
. - Полезно для отслеживания состояния, добавить
lastval
реактивное значение. - Наконец,
useval <- eventReactive({},{})
блок, зависящий от каждого из синхронизируемых элементов.
Конечный продукт (исключая ui
компонент, изменений там нет):
defbpm <- 60 # new
server <- function(input, output, session) {
output$bpm_numeric = renderUI({
numericInput(
"bpm_numeric",
"BPM Numeric",
value = defbpm, min = 40, max = 300 # update
)
})
output$bpm_slider = renderUI({
sliderInput(
"bpm_slider",
"BPM Slider",
value = defbpm, min = 40, max = 300 # update
)
})
output$numeric_val <- renderText({ useval() }) # update
output$slider_val <- renderText({ useval() }) # update
lastval <- shiny::reactiveVal(defbpm) # new
useval <- eventReactive({
input$bpm_numeric
input$bpm_slider
}, {
newval <- setdiff(c(input$bpm_numeric, input$bpm_slider), lastval())
if (length(newval) > 0) {
if (newval != input$bpm_numeric) updateNumericInput(session, "bpm_numeric", value = newval)
if (newval != input$bpm_slider) updateSliderInput(session, "bpm_slider", value = newval)
lastval(newval)
}
lastval()
}) # new
}
Способы улучшить это (на данный момент я не знаю):
- программно определите элементы для синхронизации, такие как
character
вектор идентификаторов; - обновление
eventReactive
чтобы условное выражение генерировалось программно, я просто не гуру — достаточно, чтобы сделать это на данный момент; и - определите во время выполнения, представляет ли данный идентификатор
numericInput
или asliderInput
(или что-то еще), чтобыupdate*
функции можно было вызывать в более общем виде.