{% include JB/setup %}

I recently had the opportunity to build two custom shiny inputs. In the past I’ve put forth a feeble effort to do this, but for this project failure was not an option. Searching the web I found a handful of excellent examples, so I’ll list them now and then dissect what I found to be difficult or unclear about the process.

Start with the description from the horse’s mouth:
https://shiny.rstudio.com/articles/building-inputs.html
This link describes how to create your custom input binding outside of a package, which may be useful.

A live example in a shiny app:
https://shiny.rstudio.com/gallery/custom-input-bindings.html

Another, different example and walk-through:
https://shiny.rstudio.com/articles/js-custom-input.html

Then look at this illuminating question at SE:
https://stackoverflow.com/questions/36899225/shiny-custom-input-binding-for-limitslider

The third link above does a pretty good job at walking through each element, but I might repeat it all and see if I can describe why at first I found this to be a challenge.

There are three parts to a custom input:

  1. First is the HTML and CSS, which is probably fairly straightforward.
  2. Second is some initial javascript to run the custom input; hopefully this is also straighforward.
  3. Third is transferring the input value to the shiny server, or vice-versa. This is the challenging part.

I felt that the third step is challenging because the documentation (the first link above) describes what methods your input needs, but not what each method does. The third link does a better job, but it doesn’t really help if you are doing this for the first time and step 3 isn’t working. The fourth link holds the key to sucess, and I’ll try to describe why.

If you write R packages with the S4 method dispatch, you know what I mean when I say that a shiny input binding allows you to create a special js class and a few methods for that class. The shiny package will repeatedly and automatically call the methods that you write, and these methods work together to trasfer input values from the client-side to the server-side of your app.

Here is the sample input from link 1, above:

var incrementBinding = new Shiny.InputBinding();
$.extend(incrementBinding, {
  find: function(scope) {
    return $(scope).find(".increment");
  },
  getValue: function(el) {
    return parseInt($(el).text());
  },
  setValue: function(el, value) {
    $(el).text(value);
  },
  subscribe: function(el, callback) {
    $(el).on("change.incrementBinding", function(e) {
      callback();
    });
  },
  unsubscribe: function(el) {
    $(el).off(".incrementBinding");
  }
});

Shiny.inputBindings.register(incrementBinding);

The new class registered here is the incrementBinding, the the minimal methods that shiny will call are find, getValue, setValue (with receiveMessage), subscribe, and unsubscribe.

The three problems I encountered were:
1. My inputId was not visible to the server.
* Solution: either the find or subscribe methods were not locating the input in the DOM.
2. The value was NULL or incorrect.
* Solution: the getValue method is not returning the correct value.
3. I couldn’t update my input value from the server.
* Solution: the setValue method is either not receiving the message or not setting the value properly.

Ultimately I spent a few hours working through the debugging console in Firefox to identify the source of each problem above. If you aren’t familiar with debugging javascript, it is very similar to debugging R code (if you use debug(), debugonce(), or browser() in R; I’ve not used trace() so I can’t compare it).

To use the console, just right click and select “inspect element”:

Or use the menu to select “web developer”, then choose “Debugger” (shortcut: Ctrl+Shift+S) from the top of the list:

In the debugger, be sure you have the proper source selected, and then locate the function you need to step through. Place a breakpoint in the function by clicking a line number. Next time the function is called, it will stop at the breakpoint you selected and you can inspect the value for each variable within the function.