2

I have downloaded and would like to incorporate in my R Shiny app the following JavaScript library: GitHub link

As you can see in the snippet below, I have incorporated the sequence-viewer.min.js file in the head of the UI. Additionally, using the tags$div(id="sequence-viewer") I am able to see the corresponding <div> tag in the source of the web page, so up to this point I think everything is fine.

Using the following code, I construct and render a sample Datatable with sequences. From the link of the last column, I would like to dynamically change the value of the var seq=new Sequence(''); with the sequences of the Datatable and draw the result in the sequence-viewer div every time a link is clicked.

library(shiny)

ui <- fluidPage(
    theme = shinytheme("yeti"),
    useShinyjs(),
    useShinyalert(),
    tags$head(tags$script(src = "handlebars.js"),
              tags$script(src = "sequence-viewer.min.js")),
    mainPanel( DT::dataTableOutput("DTtable"),
               br(),
               tags$div(id="sequence-viewer")
             )
    )

server <- function(input, output) {

exp1 <- reactive({
    tbl <- as.data.frame(c("MALWMPGPGAGSL", "MALKYTFDCVBJUYGFGPGAGSL", "IUYTFVBNMKIUYF"))
    names(tbl) <- "Sequence"
    tbl$link <- createLink(tbl$Sequence)
    return(tbl)
})

createLink <- function(val) {
    link <- paste0("<a href='#' onclick='var seq=new Sequence('",val,"'); seq.render('#sequence-viewer');'>Show seq</a>", sep="")
    return(link)
}

output$DTtable <- DT::renderDataTable({ exp1() 
    }, escape = FALSE, options = list(scrollX = TRUE, dom = 'lfrtip', pageLength = 10, 
lengthMenu=c(10, 25, 50, 100)), rownames = FALSE)

}

# Run the application 
shinyApp(ui = ui, server = server)

I have read many threads and tutorials on how to run custom javascript code in R Shiny, but all the examples that I've found make the call in the ui , not in the server side, could you please advice me how to get the desired output?

NOTE: According to the instructions on github page, the dependencies jquery, handlebars and bootstrap.min.css are required. I suppose that only the handlebars.js has to manually be added given that R Shiny comes already with bootstrap and jquery?

UPDATE 1: Ok, I think I'm now almost there. Thanks to the comments of @YBS I managed to get the idea of how to work with external javascript libraries. The code below works fine if I click the actionLink but it does not work when clicking the custom links inside the Datatable that I create using the createLink function. I get the following exception in the console of the browser: Uncaught ReferenceError: js$seque is not defined. Any ideas why this is happening?

library(shiny)
library(shinyjs)

jsCode = '
shinyjs.seque = function(params) {
  var defaultParams = {
   seq : "LKJHGFVBJKUGFKGFFGHJKUYTRFGHJUYTGHJIUYT"
 };
 params = shinyjs.getParams(params, defaultParams);

 var seq=new Sequence(params.seq);
 seq.render("#sequence-viewer");
}
'


ui <- fluidPage(
useShinyjs(),
extendShinyjs(text = jsCode, functions = c("seque")),
tags$head(tags$script(src = "handlebars.js"),
          tags$script(src = "sequence-viewer.min.js")
          ),

mainPanel( DT::dataTableOutput("DTtable"),
           br(),
           actionLink(inputId = "action", 
                        label = "action"),
           br(),
           tags$div(id="sequence-viewer")
    )

 )

server <- function(input, output) {

exp1 <- reactive({
    tbl <- as.data.frame(c("MALWMPGPGAGSL", "MALKYTFDCVBJUYGFGPGAGSL", "IUYTFVBNMKIUYF"))
    names(tbl) <- "Sequence"
    tbl$link <- createLink(tbl$Sequence)
    return(tbl)
})

createLink <- function(val) {
    link <- paste0("<a href='#' onclick='js$seque(",val,")' id='",val,"' class='action-button shiny-bound-input'>Show sequence</a>", sep="")
    return(link)
}

observeEvent(input$action, {
    js$seque("MALKYTFDCVBJUYGFGPGAGSL")
})

output$DTtable <- DT::renderDataTable({ 
    exp1()
}, escape = FALSE, options = list(scrollX = TRUE, dom = 'lfrtip', pageLength = 10, 
                                  lengthMenu=c(10, 25, 50, 100)), rownames = FALSE)

 }

# Run the application 
shinyApp(ui = ui, server = server)

UPDATE 2:

After many hours of debbugging I managed to solve the issue by replacing the onclick='js$seque(",val,")' event of the button in the createLink function with the following: onclick='shinyjs.seque(\"",val,"\")' - or alternatively even clearer onclick='shinyjs.seque(JSON.stringify(val))'

In short, the js$seque call was incorrect at this point, I had to replace this line with shinyjs.seque, namely with the actual name of the function in JS. On the other hand, a typical R actionButton element requires js$seque. I will try to write a clear MRE code and provide it as an answer of this thread.

gkoul
  • 1,067
  • 1
  • 10
  • 19
  • Could you please share any relevant link that I could read more? – gkoul Oct 27 '20 at 23:58
  • Please take a look here: https://stackoverflow.com/questions/64159670/replacing-initial-dataframe-from-an-editable-datatable-and-using-that-new-data-f/64161489#64161489, and here : https://stackoverflow.com/questions/64277527/vertical-alignment-of-numericinput-in-dt/64278584#64278584 – YBS Oct 28 '20 at 00:06
  • Also, please look at https://deanattali.com/shinyjs/ – YBS Oct 28 '20 at 00:10
  • Thanks for the links. I think that the first two links are not very relevant to my problem, the third one of @DeanAttali is useful, but all the provided examples (even the advanced) are based on user-defined JS functions, not external JS libraries. – gkoul Oct 28 '20 at 00:48
  • @YBS please have a look, I have updated my initial question – gkoul Oct 28 '20 at 02:54
  • Sorry, I am not good in javascript. That is why I suggested to look at Stephane Laurent and Dean Attali. – YBS Oct 28 '20 at 03:28

1 Answers1

2

The usage of shinyjs is an overkill in your case, because you do not want to call the JS function from R but anyways through the client. Thus you can simply use plain JavaScript like in this toy example:

library(shiny)

js <- HTML("seque = function(seq) {
               alert(seq);
           }")

ui <- fluidPage(tags$head(tags$script(js)), 
                tags$a(hreg = "#", onclick = "seque('test message')",
                       "Click me to send test message"))

server <- function(...) {}

shinyApp(ui, server)

Don't get me wrong shinyjs: has its merits, but the typical use case is that you want to trigger JavaScript code from the R side like in this example:

library(shiny)
library(shinyjs)

js_code <- HTML("shinyjs.seque = function(par) {
               var def_par = {seq: 'test message'};
               par = shinyjs.getParams(par, def_par);
               alert(par.seq);
           }")

ui <- fluidPage(useShinyjs(),
                extendShinyjs(text = js_code, functions = "seque"),
                actionButton("click", "Click me to send message"))

server <- function(input, output, session) {
   observeEvent(input$click,  {
      js$seque("test message from R")
   })
}

shinyApp(ui, server)

In this example I use shinyjs to call JS directly from R, while in the previous example the JS is called via the onclick.

For your example you could use an actionLink in your table and add an observer and in this you call js$queue but since you will have one link per line this may be tricky (yet not impossible) to code (basically you need dynamic listeners),

Thus, relying on pure JS (onclick) as in your example may be the better option, but then you don't need shinyjs.

thothal
  • 16,690
  • 3
  • 36
  • 71