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 functionxand variablesaandb, then we can use the dot notation ofx.aandx.bin 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
idto be able to get it back from the server. This can be accomplished using thesession$nsfunction. 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)
})
}