Custom input example |
function(input, output, session) {
output$urlText <- renderText({
as.character(input$my_url)
})
observe({
# Run whenever reset button is pressed
input$reset
# Send an update to my_url, resetting its value
updateUrlInput(session, "my_url", value = "http://www.r-project.org/")
})
}
source("url-input.R")
fluidPage(
titlePanel("Custom input example"),
fluidRow(
column(4, wellPanel(
urlInput("my_url", "URL: ", "http://www.r-project.org/"),
actionButton("reset", "Reset URL")
)),
column(8, wellPanel(
verbatimTextOutput("urlText")
))
)
)
# This function generates the client-side HTML for a URL input
urlInput <- function(inputId, label, value = "") {
tagList(
# This makes web page load the JS file in the HTML head.
# The call to singleton ensures it's only included once
# in a page.
shiny::singleton(
shiny::tags$head(
shiny::tags$script(src = "url-input-binding.js")
)
),
shiny::tags$label(label, `for` = inputId),
shiny::tags$input(id = inputId, type = "url", value = value)
)
}
# Send an update message to a URL input on the client.
# This update message can change the value and/or label.
updateUrlInput <- function(session, inputId,
label = NULL, value = NULL) {
message <- dropNulls(list(label = label, value = value))
session$sendInputMessage(inputId, message)
}
# Given a vector or list, drop all the NULL items in it
dropNulls <- function(x) {
x[!vapply(x, is.null, FUN.VALUE=logical(1))]
}
// URL input binding
// This input binding is very similar to textInputBinding from
// shiny.js.
var urlInputBinding = new Shiny.InputBinding();
// An input binding must implement these methods
$.extend(urlInputBinding, {
// This returns a jQuery object with the DOM element
find: function(scope) {
return $(scope).find('input[type="url"]');
},
// return the ID of the DOM element
getId: function(el) {
return el.id;
},
// Given the DOM element for the input, return the value
getValue: function(el) {
return el.value;
},
// Given the DOM element for the input, set the value
setValue: function(el, value) {
el.value = value;
},
// Set up the event listeners so that interactions with the
// input will result in data being sent to server.
// callback is a function that queues data to be sent to
// the server.
subscribe: function(el, callback) {
$(el).on('keyup.urlInputBinding input.urlInputBinding', function(event) {
callback(true);
// When called with true, it will use the rate policy,
// which in this case is to debounce at 500ms.
});
$(el).on('change.urlInputBinding', function(event) {
callback(false);
// When called with false, it will NOT use the rate policy,
// so changes will be sent immediately
});
},
// Remove the event listeners
unsubscribe: function(el) {
$(el).off('.urlInputBinding');
},
// Receive messages from the server.
// Messages sent by updateUrlInput() are received by this function.
receiveMessage: function(el, data) {
if (data.hasOwnProperty('value'))
this.setValue(el, data.value);
if (data.hasOwnProperty('label'))
$(el).parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
$(el).trigger('change');
},
// This returns a full description of the input's state.
// Note that some inputs may be too complex for a full description of the
// state to be feasible.
getState: function(el) {
return {
label: $(el).parent().find('label[for="' + $escape(el.id) + '"]').text(),
value: el.value
};
},
// The input rate limiting policy
getRatePolicy: function() {
return {
// Can be 'debounce' or 'throttle'
policy: 'debounce',
delay: 500
};
}
});
Shiny.inputBindings.register(urlInputBinding, 'shiny.urlInput');