{% 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:
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
.
find
:
getValue
:
setValue
:
receiveMessage
.subscribe
:
unsubscribe
:
receiveMessage
:
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.