2

I am trying to use Shiny. I am used to other programming languages (little bit of Java from App Development) where there are onClick Events, that trigger changes of other variables. I know that in MVC after a change in a view element, the controller can change the model variables in the background, then update the view without triggering an infinite loop of events on other elements.

The code here works, but it triggers unnecessary and unwanted loops:

If you choose 49 as n, dozens gets changed to 4, which changes n to 48, which changes dozens to 4, which doesn't trigger a change event.

If you then change n to 49, dozens changes to 4, which doesn't trigger a change event.

It seems wasteful and resource-intensive..

library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "dozens",
              label = "Dozens:",
              min = 1,
              max = 5,
              value = 2),
  sliderInput(inputId = "n",
              label = "n:",
              min = 1,
              max = 60,
              value = 24)
)
server <- function(input, output,session) {
  observeEvent(input$dozens,{
    updateSliderInput(session,"n",value=12*input$dozens)
  })
  observeEvent(input$n,{
    updateSliderInput(session,"dozens",value=round(input$n/12))
  })
}

shinyApp(ui = ui, server = server)

What is the correct way?

Tobias
  • 141
  • 12
  • Not sure, but maybe you can do `observeEvent(list(input$dozens,input$n),{ ...... }` and group the two updateSliderInput in place of `.......`. Sorry if it is silly, I have not tried. – Stéphane Laurent Aug 30 '19 at 07:39
  • Thanks for the idea. Then I would need to distinguish, which of the two values changed inside the expression. How do I do that? – Tobias Aug 30 '19 at 07:42
  • Ah yes sorry, not a good idea. I need to wake up. I know a similar question has been asked a couple of days ago. – Stéphane Laurent Aug 30 '19 at 07:47
  • 1
    [Here](https://stackoverflow.com/questions/56770222/get-the-event-which-is-fired-in-shiny) I described a possibilty to distinguish which event triggered an observer. Or [here](https://github.com/rstudio/shiny/issues/2514#issuecomment-506289380) a simplified version. – ismirsehregal Aug 30 '19 at 09:03

1 Answers1

0

observeEvent is (sort of) an antipattern in Shiny. Do not get me wrong, it is very useful, indispensable, I use it all the time. But the reactivity usually works better in Shiny by not caring about it and only writing the computations in a descriptive manner.

In OP's case, though, I agree it does not seem possible.
And circular dependencies between inputs are especially tricky.

A solution that is not generic but could work in OP's case is to add a simple if():

server <- function(input, output, session) {
  observeEvent(input$dozens, {
    if (round(input$n / 12) != input$dozens) {
      updateSliderInput(session, "n", value = 12 * input$dozens)
    }
  })

  observeEvent(input$n, {
    updateSliderInput(session, "dozens", value = round(input$n / 12))
  })
}
Aurèle
  • 12,545
  • 1
  • 31
  • 49
  • Thanks for the advice. I think, an abstract reformulation of my question is now, if an element can be both user input and output of calculated information, and it seems it is not advised in Shiny applications. My code example is only an abstraction of my real problem, and as the functions relating the variables "n" and "dozens" are more complicated, I would prefer not to execute them often. – Tobias Aug 30 '19 at 09:41
  • I understand. Could you think of a slightly more complicated example, that makes the proposed solution unfit, so that I can think more about it? I am also thinking of another Shiny "antipattern" that allows finer control of state management, "reactive values", but it's hard to come up with an example in the abstract, and maybe that is not your use case at all. – Aurèle Aug 30 '19 at 09:51
  • I can even tell you my real problem. I got the result of a machine learning algorithm, in it's easiest form a data frame with columns for the true label (factor with levels A and B) and the float value of values between 0 and 1 for the classification. In the triple (threshold,sensitivity,specificity), one of each values determines the other two. The threshold value then results in classification and plots. I have functions for calculating each of the three values from each of the others.User shouldbe able to give on of the three and see the resulting values for the other two instantly. – Tobias Aug 30 '19 at 12:05
  • I see. I don't know a clean solution to this problem. What you came up with is the pattern I see most commonly used for this. (possibly with adaptations such as in my answer). What @ismirsehregal suggests above looks interesting, but I haven't tried it and I don't know about the robustness. – Aurèle Aug 30 '19 at 14:22