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:
Then, you can call it from the server side:
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:
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(){
var name = prompt("Who are you?");
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.
The `this` object here refers to the clicked element*/
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 , {
cli::cat_rule("User name:")
print(input$username)
})
# This will print the id of the last clicked plot
observeEvent( input$last_plot_clicked , {
cli::cat_rule("Last plot clicked:")
print(input$last_plot_clicked)
})
Which will give:
> golex::run_app()
Loading required package: shiny
Listening on http://127.0.0.1:5495
── User name: ─────────────────────────────────────────────────────
[1] "Colin"
── Last plot clicked: ─────────────────────────────────────────────
[1] "plota"
── Last plot clicked: ─────────────────────────────────────────────
[1] "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);
})
});
mod_my_first_module_ui <- function(id){
ns <- NS(id)
tagList(
actionButton(
ns("showname"), "Enter your name"
)
)
}
mod_my_first_module_server <- function(input, output, session){
ns <- session$ns
# Whenever the button is clicked,
# we call the CustomMessageHandler
observeEvent( input$showname , {
# Calling the "whoareyou" handler
golem::invoke_js(
"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 , {
cli::cat_rule("Username is:")
print(input$name)
})
}