Как наблюдать и отслеживать одно значение из двух элементов renderUI в shiny

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

Приложение Shiny

ПРИМЕЧАНИЕ: В настоящее время я использую uiOuput() renderUI() update___Input() , это не обязательно требование. Я открыт для других подходов при условии, что пользовательский интерфейс ввода остается синхронизированным, у меня есть одна копия синхронизированного вывода.

Комментарии:

1. Если вам нужно ответить только на один из них, почему у вас есть два renderText блока? observeEvent Блоки гарантируют, что они остаются синхронизированными, оттуда все, что нуждается в этом значении, должно иметь возможность зависеть только от одного из них.

2. почему бы вам просто не удалить один из renderText выходных данных?

3. @r2evans — Полагаю, я хочу обобщить, а не просто «выбрать» одно из двух? Я согласен, что выбор произвольный, но я хотел бы думать, что есть какой-то метод абстрагирования от этого, а не привязки к одному конкретному выбору?

4. Возможно, я что-то упускаю из того, что вы говорите, но для меня общим решением является именно «зависеть только от одного из них». В SQL у вас есть coalesce , который выдает вам только первый ненулевой из его аргументов; похоже, вы хотите что-то похожее на это здесь. Честно говоря, я не нахожу в этом никакой пользы, поскольку: (1) я знаю идентификатор всех из них, поэтому могу выбрать один, и (2) Я контролирую актуальность всех кандидатов. В более сложном примере попытка условно зависеть от более чем одного может легко привести к повторным зависимостям, вызывая в лучшем случае мерцание, возможно, ненужные вычисления.

5. Вы пытаетесь обобщить, «как использовать это число», или как убедиться, что «2 или более элементов пользовательского интерфейса синхронизированы с одним и тем же значением»?

Ответ №1:

Вот мысль. Он способен немного обобщать, но все равно зависит от того, какие идентификаторы вы хотите объединить таким образом.

Изменения к вашему текущему примеру:

  1. Определите глобальное defbmp <- 60 , полезно, чтобы убедиться, что все элементы начинаются с одного и того же места.
  2. За пределами нового useval реактивного замените все ссылки на input$bpm_numeric или _slider на useval() .
  3. Полезно для отслеживания состояния, добавить lastval реактивное значение.
  4. Наконец, 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

}
  

Способы улучшить это (на данный момент я не знаю):

  1. программно определите элементы для синхронизации, такие как character вектор идентификаторов;
  2. обновление eventReactive чтобы условное выражение генерировалось программно, я просто не гуру — достаточно, чтобы сделать это на данный момент; и
  3. определите во время выполнения, представляет ли данный идентификатор numericInput или a sliderInput (или что-то еще), чтобы update* функции можно было вызывать в более общем виде.