R Shiny:如何从数据表中的自定义按钮调用 JavaScript 函数

Posted

技术标签:

【中文标题】R Shiny:如何从数据表中的自定义按钮调用 JavaScript 函数【英文标题】:R Shiny: how to call a JavaScript function from a custom button inside a Datatable 【发布时间】:2021-02-10 07:19:58 【问题描述】:

我已经下载并希望在我的 R Shiny 应用程序中加入以下 javascript 库:GitHub link

正如您在下面的 sn-p 中看到的,我已将 sequence-viewer.min.js 文件合并到 UI 的头部。另外,使用tags$div(id="sequence-viewer"),我可以在网页的源代码中看到相应的<div>标签,所以到目前为止我认为一切都很好。

使用以下代码,我构建并呈现了一个带有序列的示例数据表。从最后一列的链接,我想用Datatable的序列动态改变var seq=new Sequence('');的值,并在每次点击链接时在sequence-viewer div中绘制结果。

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)

我已经阅读了许多关于如何在 R Shiny 中运行自定义 javascript 代码的线程和教程,但是我发现的所有示例都在 ui 中调用,而不是在 server 端调用请您告诉我如何获得所需的输出?

注意: 根据 github 页面上的说明,需要依赖 jqueryhandlebarsbootstrap.min.css。我想只有 handlebars.js 必须手动添加,因为 R Shiny 已经带有 bootstrap 和 jquery?

更新 1: 好的,我想我现在差不多了。感谢@YBS 的cmets,我设法了解了如何使用外部javascript 库。如果我单击actionLink,下面的代码可以正常工作,但在单击我使用createLink 函数创建的数据表中的自定义链接时它不起作用。我在浏览器的控制台中收到以下异常:Uncaught ReferenceError: js$seque is not defined。任何想法为什么会发生这种情况?

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)

更新 2:

经过数小时的调试,我设法通过将 createLink 函数中按钮的 onclick='js$seque(",val,")' 事件替换为以下内容来解决问题:onclick='shinyjs.seque(\"",val,"\")' - 或者更清晰的 onclick='shinyjs.seque(JSON.stringify(val))'

简而言之,此时js$seque调用不正确,我不得不将这一行替换为shinyjs.seque,即用JS中函数的实际名称。另一方面,典型的 R actionButton 元素需要 js$seque。我将尝试编写一个清晰的 MRE 代码并将其作为该线程的答案提供。

【问题讨论】:

能否分享任何相关链接,我可以阅读更多内容? 请看这里:***.com/questions/64159670/…,这里:***.com/questions/64277527/… 另外,请看deanattali.com/shinyjs 感谢您的链接。我认为前两个链接与我的问题不太相关,@DeanAttali 的第三个链接很有用,但所有提供的示例(甚至是高级示例)都是基于用户定义的 JS 函数,而不是外部 JS 库。跨度> @YBS 请看一下,我已经更新了我最初的问题 【参考方案1】:

在您的情况下,shinyjs 的使用是一种过度杀伤力,因为您不想从 R 调用 JS 函数,而是通过客户端调用。因此,您可以像这个玩具示例一样简单地使用纯 JavaScript:

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)

不要误会我的意思shinyjs:有其优点,但典型的用例是您想从 R 端触发 JavaScript 代码,如下例所示:

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)

在这个例子中,我使用shinyjs直接从R调用JS,而在前面的例子中,JS是通过onclick调用的。

对于您的示例,您可以在表中使用 actionLink 并添加一个观察者,在此您调用 js$queue 但由于每行都有一个链接,因此编码可能会很棘手(但并非不可能)(基本上你需要动态监听器),

因此,在您的示例中依赖纯 JS (onclick) 可能是更好的选择,但是您不需要 shinyjs

【讨论】:

以上是关于R Shiny:如何从数据表中的自定义按钮调用 JavaScript 函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在Shiny R中丢弃DT :: datatable上的用户编辑

如何根据 R Shiny 中的单选按钮显示输出?

在 R Shiny App 中,如何在首次调用 App 时呈现默认表格?

从flex3中的自定义按钮调用放大和缩小上下文菜单

如何使用 R Shiny 中的 DT 包格式化数据表输入?

在 R Shiny 中,如何使用 actionButton 重置 rhandsontable 中的数据(反转所有手动输入)?