如何使用动态选项为Shiny selectizeInput添加书签?

时间:2018-04-08 22:22:41

标签: shiny selectize.js

Shiny提供了一个包裹selectizeInput的{​​{1}},它会生成一个文本输入/组合框小部件。我有几个原因要延迟加载selectizeInput的选项/值:可能有一长串值或可能的值取决于其他参数。此外,我希望用户能够创建新值(例如,选择现有值或创建自己的值)。

但是在所有这些情况下,selectizeInput的值不能使用Shiny的服务器端书签加以书签。初始化selectizeInput对象时,存储在selectize.js中的书签值不是有效的选项值。

是否有人为selectizeInput值添加书签有明确的解决方法?

这是处理input选项的最简单示例:

create

如果在选择框中键入新值,请点击书签按钮更新浏览器位置栏中的URL,然后点击重新加载,则该创建的值将丢失。对现有选项尝试相同,当然也可以。

如果选项加载延迟,则会变得更复杂,但问题是相同的,即在library(shiny) ui <- function(request) { fluidPage( selectizeInput("foo", "Created Values Cannot Be Bookmarked", choices=c("Choose From List or Type New Value"="", "bar", "baz"), options=list(create=TRUE)), bookmarkButton("Update URL") )} server <- function(input, output) { onBookmarked(function(url) { updateQueryString(url) }) } shinyApp(ui = ui, server = server, enableBookmarking = "server") 呈现时用户的选择无效。

2 个答案:

答案 0 :(得分:0)

这是一个解决方法黑客,扩展了原始问题的示例。在这个例子中,我也显示延迟加载。

selectizeInput的重复值存储在隐藏文本输入中。加载书签值时,隐藏备份值用于恢复真实值。要设置不在当前选项中的值,请使用shinyjs调用selectize.js API至addOption,然后调用addItem。请注意输入$ foo观察者上的ignoreInit=TRUE,以便在加载时不会覆盖隐藏的备份。另一方面,在输入$ foo.bak观察者上设置once=TRUE,以便隐藏备份仅用于在启动时更新实际值。请参阅observeEvent的文档。

仅当焦点在输入上以加速初始页面加载时,才会异步加载selectizeInput的选项。带有选项的JSON文件存储在本地www /目录中。

此示例还允许多项选择。选定的项目以逗号分隔的方式存储在隐藏文本字段中,然后在初始化期间进行扩展。

library(shiny)
library(shinyjs)

# create JSON file with selectizeInput choices to be retrieved via ajax during load
# normally this would be run once outside of app.R
dir.create("www")
foo.choices.json <- paste('{ "cars": [\n', paste0('{ "value": "', rownames(mtcars), '", "label": "',rownames(mtcars), '" }', collapse=",\n" ), "\n]}")
writeChar(foo.choices.json, "www/foo.choices.json", eos=NULL)

# javascript to set one or more values
jsCode <- "shinyjs.setfoo = function(params){ 
  x=$('#foo')[0].selectize; 
  $.each(params.items, function(i,v) { x.addOption({value:v,label:v}); x.addItem(v) })
}"

# javascript to retrieve the choices asynchronously after focus
foo.load <- "function(query, callback) {
  if (query.length) return callback(); // not dynamic by query, just delayed load
  $.ajax({
    url: 'foo.choices.json',
    type: 'GET',
    error: function() { callback() },
    success: function(res) { callback(res.cars) }
  })
}"

ui <- function(request) {
  fluidPage(
    useShinyjs(), 
    extendShinyjs(text=jsCode),
    titlePanel("Bookmarking Created Values"),
    selectizeInput("foo", NULL, 
                   choices=c("Choose From List or Type New Value"=""),
                   multiple=TRUE,
                   options=list(create=TRUE, preload="focus", load=I(foo.load))),
    bookmarkButton("Update URL"),
    div(style="display:none", textInput("foo.bak", NULL))

  )}

server <- function(input, output, session) {
  onBookmarked(function(url) {
    updateQueryString(url)
  })
  observeEvent(input$foo, { 
    if (is.null(input$foo)) {
      updateTextInput(session, "foo.bak", value="")
    } else {
      updateTextInput(session, "foo.bak", value=input$foo)
    }
  }, ignoreInit=TRUE, ignoreNULL = FALSE)
  observeEvent(input$foo.bak, {
    js$setfoo(items=as.list(strsplit(input$foo.bak,",")[[1]]))
  }, once=TRUE)
}


shinyApp(ui = ui, server = server, enableBookmarking = "server")

答案 1 :(得分:0)

使用服务器端支持进行选择是一个更好的解决方案。支持create = TRUE选项的重要步骤是使用onRestored用用户创建的新值来更新选择。

library(shiny)

init.choices <- c("Choose From List or Type New Value"="")

ui <- function(request) {
  fluidPage(
    titlePanel("Bookmarking Created Values"),
    selectizeInput("foo", NULL, 
                   choices=init.choices,
                   multiple=TRUE,
                   options=list(create=TRUE)),
    bookmarkButton("Update URL")

  )}

server <- function(input, output, session) {
  updateSelectizeInput(session, "foo", choices=c(init.choices, rownames(mtcars)), server=TRUE)
  onBookmarked(function(url) {
    updateQueryString(url)
  })
  onRestored(function(state) {
    updateSelectizeInput(session, "foo", selected=state$input$foo, choices=c(init.choices, rownames(mtcars), state$input$foo), server=TRUE)
  })
}


shinyApp(ui = ui, server = server, enableBookmarking = "server")