I am trying to understand the required architecture of a Shiny Golem app that utilises a html template and invokes some basic JavaScript on the press of a button. I think that I might be placing the index.html file in the wrong location, and I have not had success on invoking the JavaScript addEventListener on the button.
I have included the html with htmlTemplate
, my understanding from reading through Engineering Production-Grade Shiny Apps (https://engineering-shiny.org/index.html) is that golem_add_external_resources()
should handle linking the ‘script.js’ (which was created with a call to golem::add_js_file('script')
during the project setup). I would assume then that script.js would load after the DOM is created.
I can see from looking at my browser’s console that the JavaScript ‘mybutton’ variable is null though, and that the addEventListener kicks the error ... ‘Uncaught TypeError: mybutton is null’.
Any help much appreciated thanks.
myproject
-- inst
-- app
-- www
-- script.js
-- index.html
-- R
-- app_config.R
-- app_server.R
-- app_ui.R
-- run_app.R
app_ui.R
#' @param request Internal parameter for `{shiny}`.
#' DO NOT REMOVE.
#' @import shiny
#' @noRd
app_ui <- function(request) {
tagList(
golem_add_external_resources(),
fluidPage(
htmlTemplate('inst/app/index.html')
)
)
}
#' @import shiny
#' @importFrom golem add_resource_path activate_js favicon bundle_resources
#' @noRd
golem_add_external_resources <- function() {
add_resource_path(
"www",
app_sys("app/www")
)
tags$head(
favicon(),
bundle_resources(
path = app_sys("app/www"),
app_title = "integrate0003"
)
)
}
app_server.R
#' @param input,output,session Internal parameters for {shiny}.
#' DO NOT REMOVE.
#' @import shiny
#' @noRd
app_server <- function(input, output, session) {}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE =edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="mybtn">clickme</button>
</body>
</html>
script.js
let mybutton = document.getElementById('mybtn');
console.log(mybutton);
mybutton.addEventListener('click', function(){
alert('clicked')
});
Launch the app with....
golem::detach_all_attached()
golem::document_and_reload()
run_app()
I found the solution at Chapter 17 of Engineering Production-Grade Shiny Apps (https://engineering-shiny.org/index.html) where it informs to wrap the JavaScript function in $(function(){}) so that the "function is launched only when the document is ready".
The script.js now looks like below, and the event kicks the alert now.
$(function(){
let mybutton = document.getElementById('mybtn');
console.log(mybutton);
mybutton.addEventListener('click', function(){
alert('clicked')
});
})