我有一个复杂的Shiny应用程序,需要使用自定义
JavaScript代码.该应用程序由在具有不同命名空间的多个位置调用的模块组成.我需要一些JavaScript代码与R代码“模块化”,即使用模块命名空间.我能够通过创建一个包含JS代码的自定义字符串并使用shinyjs :: runjs()函数执行它(下面的示例).举个例子,这是一个公平的解决方案.但是,将更复杂的超过百行JavaScript代码放入粘贴有标识符的字符串中似乎非常容易出错并且不是最佳解决方案(缺少突出显示,痛苦的格式化等).有没有更好的方法来达到同样的效果?
library(shiny)
library(shinyJS)
myModuleUI <- function(id) {
ns <- NS(id)
tagList(
div(id = ns("clickableElement"), class = "btn btn-primary", "Click Me"),
div(id = ns("highlightableElement"), "This (and only this!) text should be highlighted on click")
)
}
myModule <- function(input, output, session) {
ns <- session$ns
shinyjs::runjs(paste0("
$('#", ns("clickableElement"), "').click(function() {
$('#", ns("highlightableElement"), "').css('background', 'yellow');
})
"))
}
ui <- fluidPage(
useShinyjs(),
tabsetPanel(
tabPanel(
"Instance 1",
myModuleUI("one")
),
tabPanel(
"Instance 2",
myModuleUI("two")
)
)
)
server <- function(input, output) {
callModule(myModule, "one")
callModule(myModule, "two")
}
shinyApp(ui = ui, server = server)
更新
为了将来的参考,我决定分享我最终实现的解决方案.我最终为每个模块创建一个JS文件,其中包含一个以名称空间作为唯一参数的函数.此函数使用此命名空间创建所有需要的对象和绑定.然后我在模块的开头使用shinyjs调用该单个函数.这允许我将JS代码保存在一个单独的文件中,该文件解决了初始问题并使代码易于管理(特别是如果你有很多JS代码).
app.R
library(shiny)
library(shinyjs)
myModuleUI <- function(id) {
ns <- NS(id)
tagList(
div(id = ns("clickableElement"), class = "btn btn-primary", "Click Me"),
div(id = ns("highlightableElement"), "This (and only this!) text should be highlighted on click")
)
}
myModule <- function(input, output, session) {
ns <- session$ns
shinyjs::runjs(paste0("myModuleJS('", ns(""), "');"))
}
ui <- fluidPage(
useShinyjs(),
tags$head(
tags$script(src = "myModuleJS.js")
),
tabsetPanel(
tabPanel(
"Instance 1",
myModuleUI("one")
),
tabPanel(
"Instance 2",
myModuleUI("two")
)
)
)
server <- function(input, output) {
callModule(myModule, "one")
callModule(myModule, "two")
}
shinyApp(ui = ui, server = server)
WWW / myModuleJS.js
function myModuleJS(ns) {
$('#' + ns + 'clickableElement').click(function() {
$('#' + ns + 'highlightableElement').css('background', 'yellow');
});
}
最佳答案 我认为有两种方法可以解决这个问题,尽管它们并不是非常优雅的解决方案.
第一种解决方案是定义一些全局函数,例如getJavascriptSelector< – function(id,session){paste0(“’#”,session $ns(id),“’”)}.这样,每次使用选择器时仍需要调用此函数,并且无法在单独的文件中定义js. 由于您运行的javascript都是字符串,另一种解决方案是您可以定义一个函数say wrapJavascriptWithNamespace(script,ns),它接受脚本字符串(或js文件)和ns对象.该函数将使用regexp匹配并用会话命名空间替换选择器.这样你就可以重用你的javascript代码,但是需要更多的工作,并且有潜在的问题,比如这段代码需要引用全局范围元素(这可以通过用一些关键字标记你的javascript来解决它一个模板).假设您将示例中使用的代码放在字符串中:
$('#[shiny-namespace]clickableElement').click(function() {
$('#[shiny-namespace]highlightableElement').css('background', 'yellow');
})
您可以轻松地解析此字符串并使用ns()替换shiny-namespace.当你需要这个字符串是常规javascrip vs模板时,你只需要编写另一个函数来去掉[shiny-namespace]标签.