Introducing sortable to add drag-and-drop to your shiny apps

by Andrie de Vries

Andrie de Vries is the author of “R for Dummies” and a Solutions Engineer at RStudio

Earlier this year I was a student on the RStudio Instructor Training, taught by the inspiratational Greg Wilson. I remember several tips from this course, for example that you should always try to draw a concept map of the material you are teaching. And, secondly, that you should make liberal use of formative assessment during your teaching.

A formative question helps the student grow their understanding of a topic, in contrast to a normative question that attempts to assess the skills of the student. Greg mentioned several archetypal formative questions, including a ranking question. In this type of question, the teacher provides the student with a list of options, and the task is to arrange these items in the correct order. It’s a great formative question, because you’re not testing the student’s memory (you provide all the terms) but you’re testing their understanding and reasoning about the topic.

This made me wonder what it would take to create ranking type questions in learnr tutorials. (A learnr tutorial is a special type of R Markdown document with a shiny runtime, that allows quizzes and other question types.)

Now, to be able to rank items in a shiny app, you first need to enable drag-and-drop behaviour in shiny. This was not something I had seen at the time (around May 2019), so I went searching…

Within hours I found a highly experimental (and incomplete) package by Kent Russell, called sortable. The sortable package was an htmlwidget that wrapped the SortableJS JavaScript library. The SortableJS library is a “JavaScript library for reorderable drag-and-drop lists”.

This sounded exactly what I needed, but there was a snag. Kent started the package during a period where he created a new htmlwidget every week, and the package itself was functioning, but incomplete. However, the existing functionality was tantalizing - it was possible to reorder all sorts of items in a the shiny UI, but it was not possible to read the return value. So you could re-order elements, but you could not use this information in a shiny reactive element. In other words, the shiny app could not respond to the input.

First steps in updating the package

My first steps in updating the package was to attempt to close the feedback loop. Specifically, I needed the JavaScript object to communicate back to R.

Fortunately, this is a well-understood problem (at least for the people who wrote htmlwidgets and shiny). And I was especially happy that Barret Schloerke was prepared to help me with some pointers.

In a nutshell, the sortable package contains a function sortable_js_capture_input() that does the translation between the htmlwidget and the shiny app.

Most users of sortable do not need to know this, but the function takes a shiny input ID as input, runs some JavaScript to modify the shiny object:

sortable_js_capture_input <- function(input_id) {
  # can call jquery as shiny will always have jquery
  inner_text <- paste0(
    "$.map(",
    "  this.el.children, ",
    get_child_id_or_text_js_fn(),
    ")",
    collapse = "\n"
  )
  js_text <- "function(evt) {
  if (typeof Shiny !== \"undefined\") {
    Shiny.setInputValue(\"%s:sortablejs.rank_list\", %s)
  }
}"

  js <- sprintf(js_text, input_id, inner_text)

  htmlwidgets::JS(js)
}

Magic can happen with sortable

Once it was possible to capture the input from SortableJS and pass this value to the shiny input object, it was possible to create some magic.

For example, in the original version of the package, Kent Russell wrote a simplified version of a shiny app that let’s the user drag-and-drop the columns of a data frame into separate lists.

Once the shiny app knows what the values of these input lists are, it is quite simple to draw a plot based on these inputs:


You can try the app at https://andrie-de-vries.shinyapps.io/sortable_drag_vars_to_plot_app/

Using the package

The package is not yet available from CRAN, but you can find the development version at https://github.com/rstudio/sortable, and the pkgdown documentation at https://rstudio.github.io/sortable/

To install the development version, use:

# install.packages("remotes")
remotes::install_github("rstudio/sortable")

And then you can try any of the examples in the documentation. For example, to run the shiny app with drag-and-drop plots, try:

shiny::runApp(system.file("shiny-examples/drag_vars_to_plot", package = "sortable"))

What happened next

I was excited about the potential of the package, so I contacted Kent Russell. In turn, he was delighted that somebody took his idea and built on it, so we agreed that I will maintain the package, with the substantial help of Barret Schloerke.

At the start of this post I said my initial inspiration was to find a way to create ranking questions in learnr tutorials.

But that’s actually another long story, and I will tell the story of custom question types and question_rank() in a follow-on to this post in the near future.

Share Comments · · · · · ·

You may leave a comment below or discuss the post in the forum community.rstudio.com.