简单的解决方案
最简单的解决方案之一可以是:
只是改变你的observeEvent(input$Select1, ...
对此的观察者
once_flag <- reactiveVal(TRUE)
observeEvent(input$Select1, {
updateSelectizeInput(
session, inputId = "Select2", server = TRUE,
choices = dat$Letter[dat$Number %in% input$Select1],
selected = if(once_flag()) input$store$Select2 else NULL
)
once_flag(FALSE)
}, ignoreInit = TRUE)
你就完成了。多么简单啊!一切问题都以同样的方式解决observeEvent
,只需一次更新调用。这once_flag
是为了确保设置Select2
仅值一次。第二次以及当你改变时Select1
,我们不设置值Select2
.
感谢@ismirsehregal等其他用户的更正,所以我可以想出上面的简单解决方案。自从shinyStore
直接为您提供了这个包装器 APIinput$store
,你不需要像我下面那样编写API,但是工作流程和后面的内容是相同的。如果您对该解决方案的工作原理感兴趣,请继续阅读。
原答案
cause
我们要解决的最大问题是server = TRUE
,阅读帮助文件我们知道store choices on the server-side, and load the select options dynamically on searching
。这意味着您的客户端(UI)一开始并不知道有哪些选项。 HTML5 localstore(shinystore背后的东西)是一种客户端技术,它只能改变一开始就存在的东西。如果启动应用程序时未给出选项,则无法更改它。这就是它失败的原因。
详细解决方案
If the select2
更新后/基于select1
,我们可以从中检索值吗shinystore
我们安顿下来之后select1
然后将值赋给select2
?
答案是否定的,也是肯定的。 不,因为原来的shinystore
没有为您提供任何用于 R-Javascript 通信的 API 来检索值。它只允许set, not get (不是真的,请参阅评论,但下面有助于理解闪亮商店的工作原理)。是的,是因为如果你了解html5 localstorage如何以及Shiny的JS-R如何通信,我们就可以编写自己的API来获取值。
这是工作流程:
- 应用程序启动,闪亮的商店更新
select1
- 服务器检测到
select1
更新了,更新了select2
的选项
- 告诉客户得到的值
select2
并从 JS 发送到 R。(在shinyStore
你可以通过以下方式访问它input$store$xxx
(输入ID)。下面,我手动编写代码,向您展示它们如何将客户端值返回到input
value.)
- 获取R中的值并更新
select2
的选择
让我们看看它在代码中是如何工作的:
1-2, R
observeEvent(input$Select1, {
# detect 1 changed
# send signal to client to get stored 2's value
session$sendCustomMessage(
"shinyStore_getvalue",
list(
namespace = "shinyStore-ex1",
key = "Select2"
)
)
# updated 2's choices based on 1
updateSelectizeInput(session, inputId = "Select2",
choices = dat$Letter[dat$Number %in% input$Select1],
server = TRUE)
}, ignoreInit = TRUE)
R-JS 如何通信,阅读此页 https://shiny.rstudio.com/articles/communicating-with-js.html.
3, JS
Shiny.addCustomMessageHandler('shinyStore_getvalue', function(data) {
var val = localStorage.getItem(`${data.namespace}\\${data.key}`);
if(val === null) return false;
val = JSON.parse(val);
if(val.data === undefined) return false;
Shiny.setInputValue(`shinystore_${data.key}`, val.data);
});
获取查询到的shinystore
value并将其作为输入值发送给Rshiny,可以直接observe
。这里不再解释详细信息,如果您想了解更多信息,请再次阅读上面的链接。
4, R
observeEvent(input$shinystore_Select2, {
updateSelectizeInput(
session,
inputId = "Select2",
choices = dat$Letter[dat$Number %in% input$Select1],
server = TRUE,
selected = input$shinystore_Select2
)
}, once = TRUE)
Add once
仅设置该值一次。
详细解决方案的完整代码
library(shiny)
library(shinyStore)
ui <- fluidPage(
headerPanel("shinyStore Example"),
tags$script(HTML(
'
Shiny.addCustomMessageHandler(\'shinyStore_getvalue\', function(data) {
var val = localStorage.getItem(`${data.namespace}\\\\${data.key}`);
if(val === null) return false;
val = JSON.parse(val);
if(val.data === undefined) return false;
Shiny.setInputValue(`shinystore_${data.key}`, val.data);
});
'
)),
sidebarLayout(
sidebarPanel = sidebarPanel(
initStore("store", "shinyStore-ex1"),
selectizeInput(inputId = "Select1", label = "Select A Number",
choices = as.character(1:3),
options = list(
placeholder = 'Please select a number',
onInitialize = I('function() { this.setValue(""); }'),
create = TRUE
))
),
mainPanel = mainPanel(
fluidRow(
selectizeInput(inputId = "Select2",
label = "Select A Letter",
choices = character(0),
options = list(
placeholder = 'Please select a number in the sidebar first',
onInitialize = I('function() { this.setValue(""); }'),
create = TRUE
)),
actionButton("save", "Save", icon("save")),
actionButton("clear", "Clear", icon("stop"))
)
)
)
)
server <- function(input, output, session) {
dat <- data.frame(
Number = as.character(rep(1:3, each = 3)),
Letter = letters[1:9]
)
observeEvent(input$Select1, {
# detect 1 changed
# send signal to client to get stored 2's value
session$sendCustomMessage(
"shinyStore_getvalue",
list(
namespace = "shinyStore-ex1",
key = "Select2"
)
)
# # updated 2's choices based on 1
updateSelectizeInput(session, inputId = "Select2",
choices = dat$Letter[dat$Number %in% input$Select1],
server = TRUE)
}, ignoreInit = TRUE)
observeEvent(input$shinystore_Select2, {
updateSelectizeInput(
session,
inputId = "Select2",
choices = dat$Letter[dat$Number %in% input$Select1],
server = TRUE,
selected = input$shinystore_Select2
)
}, once = TRUE)
observe({
if (input$save <= 0){
updateSelectizeInput(session, inputId = "Select1", selected = isolate(input$store)$Select1)
}
})
observe({
if (input$save <= 0){
req(input$Select1)
updateSelectizeInput(session, inputId = "Select2", selected = isolate(input$store)$Select2)
}
})
observe({
if (input$save > 0){
updateStore(session, name = "Select1", isolate(input$Select1))
updateStore(session, name = "Select2", isolate(input$Select2))
}
})
observe({
if (input$clear > 0){
updateSelectizeInput(session, inputId = "Select1",
options = list(
placeholder = 'Please select a number',
onInitialize = I('function() { this.setValue(""); }'),
create = TRUE
))
updateSelectizeInput(session, inputId = "Select2",
choices = character(0),
options = list(
placeholder = 'Please select a number in the sidebar first',
onInitialize = I('function() { this.setValue(""); }'),
create = TRUE
))
updateStore(session, name = "Select1", NULL)
updateStore(session, name = "Select2", NULL)
}
})
}
shinyApp(ui, server)