Chapter 7 Custom Inputs Binding
Shiny allows to extend its inputs with new input bindings.
To do that, you’ll need:
A custom binding defined in JS
A custom input in HTML (can be done with R)
7.1 Example: Meter binding
www/binding.js
// Define an event on click
$(document).on("click", ".meter", function(evt) {
var el = $(evt.target);
// Change the value when clicked
el.text(parseInt(el.text()) + 1);
el.attr("value", parseInt(el.attr("value")) + 1);
// Trigger an event
el.trigger("fire");
});
// Create a new InputBinding object
var meter = new Shiny.InputBinding();
// Extend this object with methods
$.extend(meter, {
// How to find the object
find: function(scope) {
return $(scope).find(".meter");
},
// How to get the value from the objet
getValue: function(el) {
return parseInt($(el).attr("value"));
},
// Used to retrieve the ID of the object
// STANDARD
getId: function(el) {
return el.id;
},
// Used to change the value when update for example
setValue: function(el, value) {
$(el).text(value);
$(el).attr("value", value);
},
// Register the event, and validate callback
subscribe: function(el, callback) {
$(el).on("fire.meter", function(e) {
callback();
});
},
// To remove the binding
unsubscribe: function(el) {
$(el).off(".meter");
},
// What to do when you use
// session$sendCustomMessage()
receiveMessage: function(el, data) {
this.setValue(el, data.value);
$(el).trigger('fire');
}
});
// Register this object as a Shiny Input
Shiny.inputBindings.register(meter);
- In Shiny
library(shiny)
meter <- function(
inputId,
value = 2
) {
tagList(
singleton(
tags$head(
tags$script(
src = "www/binding.js"
)
)
),
tags$meter(
id = inputId,
value = as.character(value),
min="0",
max="10",
class = "meter",
as.character(value)
)
)
}
update_meter <- function(
session,
inputId,
value
){
session$sendInputMessage(inputId, list(value = value))
}
ui <- function(){
addResourcePath("www", "www")
fluidPage(
meter("plop"),
actionButton("go","Restore")
)
}
server <- function(input, output, session) {
observeEvent(input$plop, {
print(input$plop)
})
observeEvent( input$go , {
update_meter(session, "plop", 0)
})
}
shinyApp(ui = ui, server = server)
7.3 Switch
www/binding.js
$(document).on("click", ".switch-toggle-switch", function(evt) {
var el = $(evt.target);
var val = parseInt(el.data("value"))
if (val == 1){
el.data("value", 0);
}
if (val === 0){
el.data("value", 1);
}
el.trigger("change");
});
var onoffBinding = new Shiny.InputBinding();
$.extend(onoffBinding, {
find: function(scope) {
return $(scope).find(".switch-toggle-switch");
},
getValue: function(el) {
return parseInt($(el).data("value"));
},
setValue: function(el, value) {
$(el).value(value);
},
subscribe: function(el, callback) {
$(el).on("change.onoffBinding", function(e) {
callback();
});
},
unsubscribe: function(el) {
$(el).off(".onoffBinding");
}
});
Shiny.inputBindings.register(onoffBinding);
- In Shiny
tags$div(
id = inputId,
`data-value` = 0,
class="switch-toggle-switch",
style = "pointer-events: all"
)