Introducing sortable to add drag-and-drop to your shiny apps
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
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.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.