Как добавить Shiny selectizeInput в закладки с помощью динамических параметров?
Блестящий обеспечивает selectizeInput
это обертывания selectize.js
, который создает виджет ввода текста / поля со списком. Есть несколько причин, по которым я бы хотел отложить загрузку параметров / значений для selectizeInput: может быть длинный список значений или возможные значения зависят от других параметров. Кроме того, я бы хотел, чтобы пользователь мог создавать новые значения (например, выбирать существующее значение или создавать свои собственные).
Но во всех этих случаях значение selectizeInput не может быть добавлено в закладки с помощью серверной закладки Shiny. Когда объект selectizeInput инициализируется, значение закладки, сохраненное в input
еще не являются действительными значениями параметров.
У кого-нибудь есть какие-нибудь умные обходные пути для закладки значений selectizeInput?
Вот самый простой пример, касающийся только create
опция:
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")
Если вы введете новое значение в поле выбора, нажмите кнопку закладки, чтобы обновить URL-адрес в строке адреса в браузере, и нажмите "Перезагрузить", то созданное значение будет потеряно. Попробуйте то же самое с существующей опцией, и она работает, конечно.
Это усложняется, если загрузка параметров задерживается, но проблема та же, что выбор пользователя недействителен, когда selectizeInput
рендеринг.
2 ответа
Вот лучшее решение, использующее серверную поддержку для выбора. Важным шагом для поддержки параметров 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")
Вот один обходной путь, расширяющий пример исходного вопроса. В этом примере я также показываю отложенную загрузку.
Двойное значение selectizeInput
хранится в скрытом текстовом вводе. Когда загруженные в закладки значения загружены, скрытое резервное значение используется для восстановления реального. Чтобы установить значение, которого нет среди текущих опций, selectize.js
API вызывается используя addOption
а потом addItem
, Обратите внимание, что ignoreInit=TRUE
на входе $foo наблюдателя, чтобы скрытая резервная копия не перезаписывалась при загрузке. С другой стороны, once=TRUE
устанавливается на входе $foo.bak обозревателя, так что скрытая резервная копия используется только для обновления реального значения при запуске. Смотрите документацию для 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")