在 shiny 中使用两个相互依赖的反应物
Using two reactives in shiny that depend on each other
我一直在尝试创建一个最多包含 3 个输入的仪表板,然后绘制一些数据。我已经完成了这一部分,但现在的要求已经改变,每次选择一个新变量时,他们也应该能够根据新输入过滤数据。到目前为止,这是我的尝试:
UI:
library(shiny)
ui <- fluidPage(
sidebarPanel(
tags$br(),
uiOutput("textbox_ui"),
uiOutput("filter_ui"),
tags$br(),
actionButton("add_btn", "Add Factor"),
actionButton("rm_btn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
服务器:
server <- function(input, output) {
# Track the number of input boxes to render
counter <- reactiveValues(n = 0)
AllInputs <- reactive({
x <- reactiveValuesToList(input)
})
observeEvent(input$add_btn, {
if(counter$n >2){
2
}else{
counter$n <- counter$n + 1
}
})
observeEvent(input$rm_btn, {
if (counter$n > 0) counter$n <- counter$n - 1
})
textboxes <- reactive({
n <- counter$n
if (n > 0) {
isolate({
lapply(seq_len(n), function(i) {
selectInput(inputId = paste0("var", i+1),
label = "",
choices = colnames(mtcars),
selected = AllInputs()[[paste0("var", i+1)]])
})
})
}
})
filterboxes <- reactive({
n <- counter$n
extrainputs <- sapply(seq_len(n), function(i) {
AllInputs()[[paste0("var", i+1)]]
})
summvar <- c(input$var1, extrainputs)
if(n > 0 ){
isolate({
lapply(1:length(summvar), function(x){
text <- summvar[x]
val_level <- unique(mtcars[[text]])
selectInput(inputId = paste0("fil",x+1),
label = paste0("Filter for ", text),
choices = val_level,
multiple = TRUE,
selected = val_level)
})
})
}
})
output$textbox_ui <- renderUI({ textboxes() })
output$filter_ui <- renderUI({ filterboxes() })
}
到目前为止,此设置出现了两个问题。第一,当它们出现在过滤器中时,我无法取消选择任何值;第二,我在服务器端看到这个警告“警告:.subset2 中的错误:无效的下标类型 'list'”。我的反应能力很差,任何建议(反应性或非反应性)将不胜感激。
正如我评论中所建议的...
library(shiny)
myfun <- function(df, var1) {
df %>% mutate(newvar = !!sym(var1)) # create newvar
}
ui <- fluidPage(
sidebarPanel(
tags$br(),
# uiOutput("textbox_ui"),
# uiOutput("filter_ui"),
tags$br(),
tags$div(id = 'placeholder'),
actionButton("add_btn", "Add Factor"),
actionButton("removeBtn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
server <- function(input, output, session) {
# Track the number of variables
numvars <- reactiveVal(0)
### keep track of elements/lines inserted and not yet removed
inserted <- c()
observeEvent(input$add_btn, {
if(input$add_btn==0) {
return(NULL)
}
else {
if (numvars()<0) {
numvars(0) # clicking on remove button too many times yields negative number; reset it to zero
}
newValue <- numvars() + 1 # newValue <- rv$numvars + 1
numvars(newValue) # rv$numvars <- newValue
# btn needs to be adjusted if removing and adding factors
if (input$removeBtn==0){
btn <- input$add_btn
}else {
if (input$add_btn > input$removeBtn) {
btn <- input$add_btn - input$removeBtn # add_btn counter does not decrease
}else btn <- numvars()
}
id <- paste0('txt', btn)
insertUI(
selector = '#placeholder',
## wrap element in a div with id for ease of removal
ui = tags$div(
selectInput(inputId = paste0("var", btn),
label = "",
choices = colnames(mtcars)
),
selectInput(inputId = paste0("fil",btn),
label = paste0("Filter for ", id),
choices = "",
multiple = TRUE),
id = id
)
)
}
# inserted <<- c(id, inserted) ## removes first one first
inserted <<- c(inserted, id) ## removes last one first
}, ignoreInit = TRUE) ## end of observeevent for add_btn
observe({
#print(numvars())
lapply(1:numvars(), function(i){
observeEvent(input[[paste0("var",i)]], {
mydf <- mtcars
mydf2 <- myfun(mydf,input[[paste0("var",i)]])
mysub <- unique(mydf2$newvar)
nam <- as.character(input[[paste0("var",i)]])
updateSelectInput(session = session,
inputId = paste0("fil",i),
label = paste0("Filter for ", nam),
choices = mysub,
selected = mysub
)
})
})
})
observeEvent(input$removeBtn, {
newValue <- numvars() - 1
numvars(newValue)
removeUI(
## pass in appropriate div id
selector = paste0('#', inserted[length(inserted)])
)
inserted <<- inserted[-length(inserted)]
print(inserted)
}, ignoreInit = TRUE)
}
shinyApp(ui = ui, server = server)
我一直在尝试创建一个最多包含 3 个输入的仪表板,然后绘制一些数据。我已经完成了这一部分,但现在的要求已经改变,每次选择一个新变量时,他们也应该能够根据新输入过滤数据。到目前为止,这是我的尝试:
UI:
library(shiny)
ui <- fluidPage(
sidebarPanel(
tags$br(),
uiOutput("textbox_ui"),
uiOutput("filter_ui"),
tags$br(),
actionButton("add_btn", "Add Factor"),
actionButton("rm_btn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
服务器:
server <- function(input, output) {
# Track the number of input boxes to render
counter <- reactiveValues(n = 0)
AllInputs <- reactive({
x <- reactiveValuesToList(input)
})
observeEvent(input$add_btn, {
if(counter$n >2){
2
}else{
counter$n <- counter$n + 1
}
})
observeEvent(input$rm_btn, {
if (counter$n > 0) counter$n <- counter$n - 1
})
textboxes <- reactive({
n <- counter$n
if (n > 0) {
isolate({
lapply(seq_len(n), function(i) {
selectInput(inputId = paste0("var", i+1),
label = "",
choices = colnames(mtcars),
selected = AllInputs()[[paste0("var", i+1)]])
})
})
}
})
filterboxes <- reactive({
n <- counter$n
extrainputs <- sapply(seq_len(n), function(i) {
AllInputs()[[paste0("var", i+1)]]
})
summvar <- c(input$var1, extrainputs)
if(n > 0 ){
isolate({
lapply(1:length(summvar), function(x){
text <- summvar[x]
val_level <- unique(mtcars[[text]])
selectInput(inputId = paste0("fil",x+1),
label = paste0("Filter for ", text),
choices = val_level,
multiple = TRUE,
selected = val_level)
})
})
}
})
output$textbox_ui <- renderUI({ textboxes() })
output$filter_ui <- renderUI({ filterboxes() })
}
到目前为止,此设置出现了两个问题。第一,当它们出现在过滤器中时,我无法取消选择任何值;第二,我在服务器端看到这个警告“警告:.subset2 中的错误:无效的下标类型 'list'”。我的反应能力很差,任何建议(反应性或非反应性)将不胜感激。
正如我评论中所建议的...
library(shiny)
myfun <- function(df, var1) {
df %>% mutate(newvar = !!sym(var1)) # create newvar
}
ui <- fluidPage(
sidebarPanel(
tags$br(),
# uiOutput("textbox_ui"),
# uiOutput("filter_ui"),
tags$br(),
tags$div(id = 'placeholder'),
actionButton("add_btn", "Add Factor"),
actionButton("removeBtn", "Remove Factor"),
tags$br(),
actionButton("make","Create Graph and Tables")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Data stuff")
)
)
)
server <- function(input, output, session) {
# Track the number of variables
numvars <- reactiveVal(0)
### keep track of elements/lines inserted and not yet removed
inserted <- c()
observeEvent(input$add_btn, {
if(input$add_btn==0) {
return(NULL)
}
else {
if (numvars()<0) {
numvars(0) # clicking on remove button too many times yields negative number; reset it to zero
}
newValue <- numvars() + 1 # newValue <- rv$numvars + 1
numvars(newValue) # rv$numvars <- newValue
# btn needs to be adjusted if removing and adding factors
if (input$removeBtn==0){
btn <- input$add_btn
}else {
if (input$add_btn > input$removeBtn) {
btn <- input$add_btn - input$removeBtn # add_btn counter does not decrease
}else btn <- numvars()
}
id <- paste0('txt', btn)
insertUI(
selector = '#placeholder',
## wrap element in a div with id for ease of removal
ui = tags$div(
selectInput(inputId = paste0("var", btn),
label = "",
choices = colnames(mtcars)
),
selectInput(inputId = paste0("fil",btn),
label = paste0("Filter for ", id),
choices = "",
multiple = TRUE),
id = id
)
)
}
# inserted <<- c(id, inserted) ## removes first one first
inserted <<- c(inserted, id) ## removes last one first
}, ignoreInit = TRUE) ## end of observeevent for add_btn
observe({
#print(numvars())
lapply(1:numvars(), function(i){
observeEvent(input[[paste0("var",i)]], {
mydf <- mtcars
mydf2 <- myfun(mydf,input[[paste0("var",i)]])
mysub <- unique(mydf2$newvar)
nam <- as.character(input[[paste0("var",i)]])
updateSelectInput(session = session,
inputId = paste0("fil",i),
label = paste0("Filter for ", nam),
choices = mysub,
selected = mysub
)
})
})
})
observeEvent(input$removeBtn, {
newValue <- numvars() - 1
numvars(newValue)
removeUI(
## pass in appropriate div id
selector = paste0('#', inserted[length(inserted)])
)
inserted <<- inserted[-length(inserted)]
print(inserted)
}, ignoreInit = TRUE)
}
shinyApp(ui = ui, server = server)