Shiny by RStudio
How to create custom input bindings

Introduction

In this tutorial we’ll learn how we can create our own custom input bindings. In a Shiny context, input bindings are mainly used to send information from the client to R. In contrast, the HTMLWidgets we created previously are examples of output bindings. Their main task is to send data from R to the browser and to output e.g. a chart or say a data table.

The simplest form of an input binding is an action button. The main purpose of an action button is to send a trigger from the client to R when we press the button.

Out of the box, Shiny offers a number of other useful input bindings e.g. a date picker or a selectize box. Please check out the UI Inputs section here for a more comprehensive overview of the available input bindings. Sometimes, however, you may want to use your own set of buttons with custom behavior. For instance, bootstrap alone already offers a vast amount of buttons, button groups, dropdown buttons etc. that you may want to use. This is typically the scenario in which you want to create an input binding such that you can easily use this functionality from R.

Creating your own toggle switch input binding

As a first example, let’s create a toggle switch, based on bootstrap switch. With bootstrap switch you can easily turn checkboxes and radio buttons into iPhone style toggle switches. An overview of the various switches can be found here.

Here’s an example of the switch we will be creating. Press the button and see what happens!

An example of a custom input binding in the form of a toggle switch

Let’s start with an example without Shiny and slowly work towards creating the input binding.

From the documentation of bootstrap switch it becomes clear that we need to:

  1. include a single CSS and JavaScript file,
  2. create a standard input control, and
  3. call the bootstrapSwitch method on the input control in JavaScript.

More concretely, for this first step we need to include:

<link href="bootstrap-switch.css" rel="stylesheet">
<script src="bootstrap-switch.js"></script>

Note that Bootstrap switch also requires jquery and bootstrap, however, these are automatically included by shiny.

Next, we need to create an input tag of type checkbox and give it an id so we can easily grab the element later:

<input id="id1" type="checkbox">

Finally, in JavaScript we select the input control via jQuery, based on the given id, and call the bootstrapSwitch method on the selection. The latter method is defined in bootstrap-switch.js.

$("#id1").bootstrapSwitch();

Bootstrap switch offers various options and methods you can use to initialize the component or to update the state after the switch has been rendered. For instance, using the options you can control the colors and the texts used in the switch. You can also listen for state change events if you need to.

Similar to creating an HTMLWidget, in order to create an input binding, we have to write some R code and some JavaScript code.

Boilerplate code

There are a few things that are good to figure out before implementing a component. Before you start, make sure you understand:

  1. the required HTML of your component and that you know which options you can set,
  2. how to create HTML attributes for these options and extract their values from JavaScript,
  3. how you can initialize your component in HTML or JavaScript based on these values,
  4. how to listen for changes to the state of your component, and
  5. how to extract a useful value out of your component to send to Shiny.

Next we’ll look into the actual R and JavaScript code required. At a first glance the required code below may appear a bit involved for only a single component. However, most of it is just boilerplate code that is the same for all input bindings you’ll create. Filling in the details usually does not require too much original code. Especially if you master the five steps from above, creating a custom input binding is not that difficult.

R boilerplate

The core boilerplate R code we need to implement is:

component <- function(inputId, ...) {
  
  tagList(
  
    # add CSS and JavaScript files
    ...

    # create required tag and set options via HTML attributes
    ...
  )
}

Basically, we create a function, pass in some options, tell Shiny what and where the required CSS and JavaScript files are, create a taglist with some tags, and set some attributes in these tags.

We can then can use this function to use our binding. For instance, we may have component("comp1", label = "comp 1", value = 10). Subsequently, we get the input back via input$comp1, which value initially will be 10.

JavaScript boilerplate

Next, the core boilerplate JavaScript code we need to implement is:

// step i
var binding = new Shiny.InputBinding();

// step ii
$.extend(binding, {

  find: function(scope) {
    ...
  },

  initialize: function(el){
    ...
  },
  
  getValue: function(el) {  
    ...
  },

  subscribe: function(el, callback) {   
    ...
  }
  
});

// step iii
Shiny.inputBindings.register(binding);

Don’t worry if you’re not familiar with this code yet, we’ll explain it shortly! Essentially, we first create a new binding object, then add a few simple methods to it, and finally, we register the component so that Shiny knows it exists.

These methods allow Shiny to find instances of our component, to initialize them, to get information (values) out of them, to listen and react to changes to the state of our components, and to send information to Shiny, which in R we can capture with input$....

We’ll discuss each of these methods and their implementation for our switch component in detail below. Let’s start with the R code!

Implementing the R part

In order to use our switch component in other projects we create a BootstrapSwitch package. For convenience, you can find the example package here.

In our example, we want to create a toggle switch from R that we can use in Shiny. We’ll allow the user to set an inputId and some options.

More specifically, we want to be able to set the inputId, label, initial state, width, size and to specify the on / off color scheme i.e. onColor and offColor, using standard bootstrap colors i.e. default, primary, success, info, warning, and danger.

The complete R code for our example looks like this:

bootstrapSwitch <- function(inputId, label, state = TRUE, width = "auto", size = "mini", 
                            onColor = "success", offColor = "danger") {

  addResourcePath(
    prefix = 'wwwFrissSwitch', directoryPath = system.file('www', package='BootstrapSwitch')
  )

  tagList(

    singleton(tags$head(
      tags$script(src="wwwFrissSwitch/bootstrap-switch.min.js"),
      tags$script(src="wwwFrissSwitch/BootstrapSwitchBinding.js"),
      tags$link(rel="stylesheet", type="text/css", href="wwwFrissSwitch/bootstrap-switch.min.css")
    )),

    tags$input(type = "checkbox", id = inputId, class = "FrissSwitch",
               "data-state" = tolower(state),
               "data-size" = size,
               "data-label-width" = width,
               "data-on-color" = onColor,
               "data-off-color" = offColor,
               "data-label-text" = label)
  )
}

As we want to create an R package, Shiny needs to know where our CSS and JavaScript files live. The addResourcePath and the code block inside singleton make sure that happens. Of note, all these files need to be placed inside the www directory of our package.

system.file tells Shiny where the directory of our package is after we have installed it, while BootstrapSwitchBinding.js contains the JavaScript part for our binding.

Next, we create an input tag and set its type, id and class. The class attribute is used in JavaScript to find all instances of our component in a Shiny app (see below). Furthermore, we add a number of data- attributes to initialize our toggle switch. See here for an overview of the available options.

Finally, note that the return type of our function is a tagList, that contains both the CSS and JavaScript files, as well as the required input tag. Shiny subsequently makes sure the files we need are included in the head of the HTML document of our app and generates the input tag. After this is done, we’ll call some JavaScript on the tag, which we’ll discuss next.

Implementing the JavaScript part

Next, let’s take a closer look at the boilerplate code for the required JavaScript:

// this object that tells Shiny how to identify instances of our component and how to interact with them 
var binding = new Shiny.InputBinding();

// add some methods to our binding object 
$.extend(binding, {

 ...
  
});

// register the binding so Shiny knows it exists
Shiny.inputBindings.register(binding);

First we indicate that we want to create a new custom input binding and store it in a variable called binding. Next, using jQuery’s extend we add various methods to this object (described below). Finally, we register our component.

Core methods

Various methods can be added to the input binding object. The following are the most important:

$.extend(binding, {

  find: function(scope) {
    ...
  },

  initialize: function(el){
    ...
  },
  
  getValue: function(el) {  
    ...
  },

  subscribe: function(el, callback) {   
      
      $(el).on('...', function(event){
    
        callback();
      });
  }
  
});

The implementation details of these methods differ for each binding. Before discussing these, we first explain their purpose.

find

The find method finds any descendant elements that are an instance of your component and return them as an array-like object. Internally, the method often uses jQuery find method to locate these instances (see below). Note the argument scope is passed automatically by Shiny and here refers to the actual document object, the root node of our app.

initialize

The initialize method can be used to initialize a component, after we have rendered the required tags. el here is the element that was returned from find i.e. the actual component. Furthermore, we can use jQuery’s data method to extract data attributes from our element (see below).

For instance, in the R code, we added attributes for data-state and data-on-color to our input element. In JavaScript, we can easily extract such attribute values from el e.g. using $(el).data("state") and $(el).data("onColor"). Note that we need to omit the data- part and use camel casing. See here for additional information on jQuery’s data method.

getValue

The getValue method is used to extract the actual value out of our element el. The value we return here is the value that is made available in Shiny via input$....

subscribe

The subscribe method listens for specific events on our component. This is easiest to implement using jQuery’s on method. When jQuery detects the event, the method uses callback (an internal Shiny function), that subsequently calls getValue, and send the extracted value to shiny.

The complete JavaScript code for our switch example looks like this:

// create a binding object
var binding = new Shiny.InputBinding();

// add methods to it using jQuery's extend method
$.extend(binding, {

  find: function(scope) {

    // find all instances of class FrissSwitch
    return $(scope).find(".FrissSwitch");

  },

  // this method will be called on initialisation
  initialize: function(el){

     // extract the state from el
     // note here our bootstrapSwitch does not yet exist
     var state = $(el).data("state");

     // initialize our switch based on the extracted state
     // note $("#" + el.id) equals the input tag we generated
     $("#" + el.id).bootstrapSwitch("state",state);
     
     // now bootstrapSwitch does exist

  },

  // this method will also be called on initialisation (to pass the intial state to input$...)
  // and each time when the callback is triggered via the event bound in subscribe
  getValue: function(el) {

    // get the value from bootstrapSwitch
    var value = $(el).bootstrapSwitch('state');

    return value;
  },

  // we want to subscribe to the switchChange event
  // see http://bootstrapswitch.com/events.html
  subscribe: function(el, callback) {

    // only when the switchChange event is detected on instances of class bootstrapSwitch
    // trigger the getValue method and send the value to shiny
    $(document).on('switchChange.bootstrapSwitch', function(event){

      // callback which will tell Shiny to retrieve the value via getValue
      callback();
    });
  }
});

// register the binding so Shiny knows it exists
Shiny.inputBindings.register(binding);

In the code above, $(document).on('switchChange.bootstrapSwitch', ...) is an example of jQuery’s event-delegation. This type of behavior allows you to be efficient with event handlers.

For instance, suppose you generated a thousand buttons and you want to know the id of a button when you click on it. You could add a thousand click handlers, one for each button, but this is very inefficient. Using event-delegation you can use a single event handler instead, while still being able to tell which button was clicked.

Here we ask jQuery to find all the elements of class bootstrapSwitch, and to use a single event handler for all switchChange events. The event object is automatically passed by JavaScript and can be used to find more information on the exact event. For example, see here.

Of note, most changes of the JavaScript code between bindings will be for the getValue and subscribe methods. Fortunately, these methods often require only a few lines of code each. Note most lines in the code blocks above are code comments and indents.

Using console.log to learn more about your code

You can find out a lot about the code above by inserting console.log messages and checking your browser console (just hit F12 in the browser).

For instance, to know more about the event, scope, or el objects as used in the various methods, you can review their console.log.

The console.log will also tell you when and how often specific functions are called. For instance, the initialization method is called only once per component instance and so is the subscribe method. However, the callback inside the subscribe method is called many times, as is the getValue function.

Additional methods

A more thorough description of the various methods above is provided here, while additional custom input binding examples by RStudio can be found here, and here.

In addition to the previously discussed methods, several other JavaScript methods can be added to your input binding object, notably; getId, getState, getRatePolicy, receiveMessage and unsubscribe. These are discussed in the various references provided and are not further discussed here.

Second example: A button group

To get some more exercise into creating your own custom input bindings, consider the source code for the binding for the button group used in the dashboard application, which can be found here.

HTMLWidgets that send events to R

HTMLWidgets send data from R to the browser, while input bindings do the reverse i.e. they send data from the client to R. Even though this is a reasonable way to think about the difference between input and output bindings, it’s not the whole story.

For instance, RStudio’s leaflet widget is an example of an HTMLWidget i.e. an output binding, that also sends events from the client to R. It does so when we change the map zoom level, pan the map to a different location, or click on a marker. Similar to input bindings, in R we can access these events using input$....

Note that in JavaScript we can always send events back to Shiny using the Shiny.onInputChange method (see tutorial 3). In the next tutorial we see how we can use this method to send information from a brushable timeline widget (based on the C3LineBarChart developed tutorial 3), in order to create a filter system for our interactive dashboard application.