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()
Advertisement
Answer
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') }); })