17.4 JavaScript <-> {shiny}
Communication
Now that we have seen some client-side optimization, i.e. R does not do anything with these events when they happen (in fact R is not even aware they happened).
17.4.1 From R to JavaScript
Calling JS from the server side (i.e. from R) is done by defining a series of CustomMessageHandler
functions: these are functions with one argument that can then be called using the session$sendCustomMessage()
method from the server side.Or if you are using {golem}
, using the invoke_js()
function.
Using the skeleton from golem::add_js_handler("first_handler")
, we get this snippet:
$( document ).ready(function() {
Shiny.addCustomMessageHandler('fun', function(arg) {
}) });
Then, you can call it from the server side:
$sendCustomMessage("fun", list())
session# OR
::invoke_js("fun", ...) golem
NOTE: the
list()
argument will be coverted to JavaScript Object Notation (JSON) and read via JavaScript. If we have functionx
and variablesa
andb
, then we can use the dot notation ofx.a
andx.b
in our JavaScript.
For Example:
// We define a handler called "computed", that can be called
// from the server side of the {shiny} application
Shiny.addCustomMessageHandler('computed', function(mess) {
// The received value (in mess) is serialized in JSON,
// so we can access the list element with object.name
alert("Computed " + mess.what + " in " + mess.sec + " secs");
})
And within R:
observe({
# Register the starting time
Sys.time()
deb <-# Mimic a long computation
Sys.sleep(
sample(1:5, 1)
)# Calling the computed handler
::invoke_js(
golem"computed",
# We send a list, that will be turned into JSON
list(
what = "time",
sec = round(Sys.time() - deb)
)
) })
17.4.2 From JavaScript to R
How can you do the opposite (send JavaScript calls to R)?
There is an object called Shiny
within your browser. This object can be used to send values to R by creating an InputValue
.
// This function from the Shiny JavaScript object
// Allows to register an input name, and a value
Shiny.setInputValue("rand", Math.random())
You bind an input which can be caut fro the server side using:
# Once the input is set, it can be caught with R using:
observeEvent( input$rand , {
print( input$rand )
})
Shiny.setInputValue
cn be used inside any JavaScript function. As an example we use the following code snippet:
- In
inst/app/www/script.js
function alertme(){
prompt("Who are you?");
var name =alert("Hello " + name + "! Welcome to my app");
Shiny.setInputValue("username", name)
}
$(function(){
// Waiting for `{shiny}` to be connected
$(document).on('shiny:connected', function(event) {
alertme();
});
$(".shiny-plot-output").on("click", function(){
/* Calling the alertme function with the id
of the clicked plot. `this` object here refers to the clicked element*/
The Shiny.setInputValue("last_plot_clicked", this.id);
}); });
The above snippet gets the user name and last plot clicked. We can catch the call to the server using the following snippet:
# We wait for the output of alertme(), which will set the
# "username" input value
observeEvent( input$username , {
::cat_rule("User name:")
cliprint(input$username)
})
# This will print the id of the last clicked plot
observeEvent( input$last_plot_clicked , {
::cat_rule("Last plot clicked:")
cliprint(input$last_plot_clicked)
})
Which will give:
> golex::run_app()
: shiny
Loading required package
://127.0.0.1:5495
Listening on http: ─────────────────────────────────────────────────────
── User name1] "Colin"
[: ─────────────────────────────────────────────
── Last plot clicked1] "plota"
[: ─────────────────────────────────────────────
── Last plot clicked1] "plotb" [
NOTE: If you are using modules, you will need to pass the namespacing of the
id
to be able to get it back from the server. This can be accomplished using thesession$ns
function. This comes by default using any golem-generated module.
Example of a session$ns
call used in Golem Module:
$( document ).ready(function() {
// Setting a custom handler that will
// ask the users their name
// then set the returned value to a Shiny input
Shiny.addCustomMessageHandler('whoareyou', function(arg) {
var name = prompt("Who are you?")
Shiny.setInputValue(arg.id, name);
})
});
function(id){
mod_my_first_module_ui <- NS(id)
ns <-tagList(
actionButton(
ns("showname"), "Enter your name"
)
)
}
function(input, output, session){
mod_my_first_module_server <- session$ns
ns <-# Whenever the button is clicked,
# we call the CustomMessageHandler
observeEvent( input$showname , {
# Calling the "whoareyou" handler
::invoke_js(
golem"whoareyou",
# The id is namespaced,
# so that we get it back on the server-side
list(
id = ns("name")
)
)
})
# Waiting for input$name to be set with JavaScript
observeEvent( input$name , {
::cat_rule("Username is:")
cliprint(input$name)
}) }