I am building a Shiny app involving simulation. The simulation itself takes up some resources, and it becomes a problem - a burden to the server - if the application is used by many users.
So, I was thinking of that hey, why not write the simulation part in JavaScript, run it in a browser, and then take the results to R. These results can be then processed on the server, plots can be drawn, etc.
Is this even possible?
Ok, let's illustrate this with a simple example: Here is a JavaScript code that creates a list of numbers: 0, 3, 6, 9, 12, 15, 18, 21, 24, 27 ...and which should be run on the browser:
let numberlist = [];
for (let i = 0; i < 10; i ) {
numberlist.push(i*3);
}
Now, if I want to plot this in Shiny, the UI. R file would probably look like this:
## ui.R ##
fluidPage(
sidebarLayout(
sidebarPanel(
#Sidebar panel can be empty, I think.
),
mainPanel(plotOutput("Plot"))
)
)
But what about the server.R?
## server.R ##
function(input, output) {
output$Plot <- renderPlot({
#What should I put here to:
#plot(c(0, 3, 6, 9, 12, 15, 18, 21, 24, 27))
#i.e. to plot(c(JavaScript result))
})
}
Is this even the way to do this, or should the results from the JavaScript code be taken to the server using the UI somehow?
CodePudding user response:
For the case you describe, you can use Shiny.setInputValue
. Create a subfolder www of your app folder, and put in this subfolder the following JavaScript file, say it is named myjs.js:
$(document).on("shiny:connected", function () {
let numberlist = [];
for (let i = 0; i < 10; i ) {
numberlist.push(i * 3);
}
Shiny.setInputValue("numbers", numberlist);
});
Include this file in ui.R:
ui <- fluidPage(
tags$head(tags$script(src = "myjs.js")),
......
)
Then in your server.R file, you will receive the list of numbers in input[["numbers"]]
. It will be a list, not an atomic vector, so you have to use unlist
:
plot(unlist(input[["numbers"]]))
Now, say you want to set the length of your list of numbers in R, and send this length to JS. You need a custom message handler in this case:
$(document).on("shiny:connected", function () {
Shiny.addCustomMessageHandler("getNumbers", function (l) {
let numberlist = [];
for (let i = 0; i < l; i ) {
numberlist.push(i * 3);
}
Shiny.setInputValue("numbers", numberlist);
});
});
Here is a scenario example:
## ui.R ##
fluidPage(
tags$head(tags$script(src = "myjs.js")),
sidebarLayout(
sidebarPanel(
numericInput("length", "Length", min = 10, max = 100, value = 10, step = 1)
),
mainPanel(plotOutput("Plot"))
)
)
## server.R ##
server <- function(input, output, session){
observeEvent(input[["length"]], {
session$sendCustomMessage("getNumbers", input[["length"]])
})
output[["Plot"]] <- renderPlot({
req(input[["numbers"]])
plot(unlist(input[["numbers"]]))
})
}