Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does R shiny know when to update inputs?

Tags:

r

shiny

I was implementing a toggle button for a checkbox group in R Shiny today and I came across something interesting that I don't fully understand. My first approach to creating the toggle button was to write a javascript script that switched the checkboxes' checked attribute, so true became false and false became true, when the user clicked the button. This worked and when I ran the shiny app and pressed the toggle button, the checkboxes were flipped. However, if I displayed the selections, using renderText(), shiny did not react to my changes. If, however, I clicked one of the checkbox groups Shiny would update to the appropriate checkboxes.

I then changed my javascript code so that instead of setting the checked attribute, it used jQuery's .click() to click each checkbox. This approach worked exactly as I hoped so that when I pressed the toggle button, the checkboxes were flipped and the text was updated. I'm curious to know why this is though. My suspicion is that shiny listens for click events but can't listen for attributes that are changed programmatically. My explanation isn't too great so I created a small shinyapps app demonstrating the difference in the toggle button behavior. The first toggle button uses .click() and works and the second one uses the checked attribute and does not update reactively. The code I used to demonstrate the differences is below:

ui.R

library(shiny)

fruit = c("apple", "banana", "orange", "pear", "peach")

shinyUI(fluidPage(
  tags$script(src = "toggle_button.js"),

  titlePanel("Test"),

  sidebarLayout(
    sidebarPanel(
      checkboxGroupInput("fruit_list",
                  "Select a fruit",
                  choices = fruit,
                  selected = fruit),

      fluidRow(
        actionButton("toggleButton_1", "Toggle 1"),
        actionButton("toggleButton_2", "Toggle 2")
      )

    ),

    mainPanel(
      textOutput("string")
    )
  )
))

server.R

library(shiny)

shinyServer(function(input, output) {

  output$string <- renderText({
    paste(input$fruit_list, sep = ", ")
  })

})

www/toggle_button.js

$(document).ready(function() {
    $("#toggleButton_1").click(function() {
      var fruit_boxes = $("#fruit_list :checkbox");

      for (var i = 0; i < fruit_boxes.length; i++) {
          fruit_boxes[i].click();
      }
    });

    $("#toggleButton_2").click(function() {
      var fruit_boxes = $("#fruit_list :checkbox");

      for (var i = 0; i < fruit_boxes.length; i++) {
          fruit_boxes[i].checked = !fruit_boxes[i].checked;
      }
    });
});
like image 827
jeromefroe Avatar asked Sep 27 '22 19:09

jeromefroe


1 Answers

If you're interested in diving into how shiny javascript works, one good resource is how to build custom input objects.

After reading that and learning how to write an input, you'll understand the code for the checkbox input. Look specifically at the subscribe function: it tells javascript to call shiny with "hey, I just updated!" when the "change" event gets triggered. So that's your answer: the change event is triggered when you manually click on an input (or using jquery click achieves a similar result), but when you simply change the value manually, the event isn't triggered.

You can test this by adding your own callback on change, and seeing that the change event only gets triggered with a click but not with a manual value change. To take this one step further, this means you can manually tell shiny to update the value by calling $(".checkbox").trigger("change"). Try adding that line after changing the value , and now it'll work (though I wouldn't do this, it's for illustration purposes)

like image 160
DeanAttali Avatar answered Sep 30 '22 07:09

DeanAttali