Output reacting on dynamically created sliderInput change

1,740 views
Skip to first unread message

Katarzyna Smaga

unread,
Nov 20, 2013, 6:35:19 AM11/20/13
to shiny-...@googlegroups.com
Hi,

I'm trying to create Shiny application, which consists of a selectInput control, dynamically created sliderInput, some static input data and output data.

There are dependencies between elements of this app:
1. slider value depends on select list value
2. output data depends on slider value and select list value

(Please see attached scripts)

Because of those dependencies, I cannot get recalculation of output data right.

There are 2 options, that I've tried:
1. make output data reactive to slider value change
2. make output data reactive to both slider and select list values change

but:
1. output won't refresh in case slider recalculation (triggered by select list change) returns the same value, as before recalculation (see change from 'vec1' to 'vec2' - slider is recalculated (max boundary changes), but 'value' stays the same)
2. output will recalculate twice if both slider and select list value change (see change from 'vec1' to 'vec3')

Is there any way to trigger output recalculation whenever slider is recalculated even if it's value after recalculation is unchanged?
Or maybe there's another solution to my problem?

I would be very grateful for any suggestions,
Katarzyna
server.R
ui.R

Stéphane Laurent

unread,
Nov 20, 2013, 3:03:02 PM11/20/13
to shiny-...@googlegroups.com
Very cool question !! This is an excellent exercise to see how renderUI({}) works ! (the double calculation)

 I have spend more than 2 hours to find a solution. By the way I have learned two things :

 - when a reactive object is recalculated but the result is the same as the previous one, then its receptors are not updated (please correct me if I'm wrong), very good to know that

 - with updateSliderInput(), we can only update the current value of the slider, not the min and the max 

My first idea was to use updateSliderInput() rather than renderUI({}) to update the slider, but as I said before, it does not allow to update the range of the slider. Finally I have found the following solution (code below). The idea is : 
 1) isolate mainData() to avoid double calculation ; thus the table output is only reactive to the slider value 
 2) construct a reactive indicator which is updated each time the data has changed but the slider value has not changed (and which is not updated in other cases)
 3) connect the table output to this reactive indicator 

I would be glad to know whether someone has an easier solution ! (I would also be glad to hear some comments about this difficulty with renderUI({}))

library(shiny)

runApp(
  list(
    server=function(input, output, session) {
      
      globalData <- data.frame("vec1" = c(1,2,3), "vec2" = c(1,1,5) , "vec3" = c(2,5,6))
      
      mainData <- reactive({
        data <- do.call("$",list(globalData,input$vector))
        return(data)
      })
      
      value_has_not_changed <- reactiveValues(x=0)
      
      output$slider_rangeInput <- renderUI({
        data <- mainData()
        newvalue <- data[1]
        slider <- isolate(input$slider)
        if(!is.null(slider)){
          if(newvalue == slider) value_has_not_changed[["x"]] <-  isolate(value_has_not_changed[["x"]]) + 1
        }
        sliderInput(inputId="slider",label = "Range", min=data[1],max = data[3], value = newvalue)
      })
      
      
      output$contents <- renderTable({
        return(globalData)
      })
      
      output$result <- renderTable({
         if(is.null(input$slider)){
           return(NULL)
         }
        w <- value_has_not_changed[["x"]] # makes the reactive connection to value_has_not_changed[["x"]]
#        data <- mainData() * slider       
        data <- isolate(mainData()) * input$slider
        print(data)
        return(data.frame(data))
      })
      
    },
    
    ui=pageWithSidebar(
      
      headerPanel("Test"),
      
      sidebarPanel(
        
        selectInput(inputId = "vector",
                    label = "Values vector",
                    choices = list("vec1","vec2","vec3"))
      ),
      
      mainPanel(
        htmlOutput("slider_rangeInput"),
        tableOutput('contents'),
        tableOutput('result')
      )
      
    )))



Stéphane Laurent

unread,
Nov 20, 2013, 4:04:42 PM11/20/13
to shiny-...@googlegroups.com
I'm trying to summarize the philosophy behind this situation. Let me write my thoughts here.

a) There's an output object  output$out = f(input$x, y())  constructed from an input object  input$x  and a reactive conductor y()
b) The input object input$x is created through a dynamic UI  output$ui_x = g(y()) constructed from the reactive conductor y()

Thus : 

 1) If we don't isolate anything, output$out is updated twice : firstly when y() is updated, but once y() is updated,the dynamic UI output$ui_x is updated and it creates a new input$x, thus output$out is updated a second time 
 2) If we isolate by doing output$out = f(input$x, isolate(y())), then there's a problem in case (*) : the case when y() changes but not g(y())

The idea behind 2) is that, although y() is isolated, the value of y() will be read whenever input$x will be updated. But in case (*), input$x is not updated, and then output$out is calculated with the "old" value of y(). 

Thus my idea was to "anticipate" case (*) : when the reactive value value_has_not_changed[["x"]] is updated, it means that we are in case (*), and then defining output$out = f(input$x, isolate(y()), value_has_not_changed[["x"]] ) does the job.



Katarzyna Smaga

unread,
Nov 21, 2013, 5:04:56 AM11/21/13
to shiny-...@googlegroups.com
Thank you so much for your help! This solution does the job indeed. 

The funny thing is, that I went through exactly the same thinking - first I was surprised, that only change of value triggers a receptor update (but then I thought, hey, that's why they call it reactiveValues, and not reactiveObjects, right?)
My next idea was also to use updateSliderInput(), and then I wanted to write something similar to your solution, but I got lost in multiple sliders and select list dependencies (in my original code I have much more than 2 of those).

Anyway, my outputs finally stopped 'blinking' and I'm so grateful for your help!
Reply all
Reply to author
Forward
0 new messages