闪亮的反应性在子模块中不起作用
Shiny reactivity not working in subModules
因为我的 shiny
应用程序变得相当大,所以我最近将一些代码放入模块中(也是为了在不同的地方多次重用代码)。不知何故,部分代码不再按预期工作。
在这个例子中,我有一个根据输入元素和 return 反应式 data.frame
过滤数据的模块。在 mainPanel 中,我有一个模块,它根据过滤后的数据创建一个 dataTable
。但是反应性不起作用,当我更改 selectInput
时,dataTable
不会更新。
library(shiny)
library(DT)
filtersUI <- function(id) {
ns <- NS(id)
selectizeInput(
ns("Species"), label = "Species",
choices = levels(iris$Species),
selected = "virginica"
)
}
filters <- function(input, output, session, .data) {
inputs <- reactive({
list("Species" = input[["Species"]])
})
reactive({
.data[.data$Species %in% inputs()[["Species"]], ]
})
}
dataTableUI <- function(id) {
ns <- NS(id)
DT::dataTableOutput(ns("data.table"))
}
dataTable <- function(input, output, session, .data) {
output$data.table <- DT::renderDataTable({
DT::datatable(.data)
})
}
appUI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
filtersUI(ns("filter"))
),
mainPanel(
dataTableUI(ns("data.table"))
)
)
}
app <- function(input, output, session, .data) {
data.subset <- callModule(filters, "filter", .data = .data)
callModule(dataTable, "data.table", .data = data.subset())
}
ui <- fluidPage(
appUI("app")
)
server <- function(input, output, session) {
callModule(app, "app", .data = iris)
}
shinyApp(ui, server)
但是当将代码从子模块复制到 app
模块时,代码工作正常:
library(shiny)
library(DT)
appUI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
selectizeInput(
ns("Species"), label = "Species",
choices = levels(iris$Species),
selected = "virginica"
)
),
mainPanel(
DT::dataTableOutput(ns("data.table"))
)
)
}
app <- function(input, output, session, .data) {
inputs <- reactive({
list("Species" = input[["Species"]])
})
data.subset <- reactive({
.data[.data$Species %in% inputs()[["Species"]], ]
})
output$data.table <- DT::renderDataTable({
DT::datatable(data.subset())
})
}
ui <- fluidPage(
appUI("app")
)
server <- function(input, output, session) {
callModule(app, "app", .data = iris)
}
shinyApp(ui, server)
我知道在这个简单的示例中,模块化结构看起来有点矫枉过正,但在我的真实应用程序中,我的模块中有很多代码,我删除了这些代码以使这个示例最小化。因此,最好有一个使用与第一个代码片段中相同的模块化结构的解决方案。知道为什么会失败吗?
你用子模块创建了一个可重现的例子,做得非常好。然而,这个问题实际上与子模块没有任何关系。您只需要以不同方式传递反应对象 data.subset
即可。而不是
callModule(dataTable, "data.table", .data = data.subset())
你应该使用
callModule(dataTable, "data.table", .data = data.subset)
传递反应本身而不是它的当前值。然后可以像这样
在 DT::renderDataTable
中“解析”该值
output$data.table <- DT::renderDataTable({
DT::datatable({.data()})
})
按照你的编码方式,“构建时”的数据,即未过滤的数据集被发送到模块,并且无法在途中观察到。
需要说明的是:注释行(## remove parantheses here
和 ## add parantheses here
)是我从您的原始代码中更改的唯一行。
library(shiny)
library(DT)
filtersUI <- function(id) {
ns <- NS(id)
selectizeInput(
ns("Species"), label = "Species",
choices = levels(iris$Species),
selected = "virginica"
)
}
filters <- function(input, output, session, .data) {
inputs <- reactive({
list("Species" = input[["Species"]])
})
reactive({
.data[.data$Species %in% inputs()[["Species"]], ]
})
}
dataTableUI <- function(id) {
ns <- NS(id)
DT::dataTableOutput(ns("data.table"))
}
dataTable <- function(input, output, session, .data) {
output$data.table <- DT::renderDataTable({
DT::datatable({.data()}) ## add parantheses here
})
}
appUI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
filtersUI(ns("filter"))
),
mainPanel(
dataTableUI(ns("data.table"))
)
)
}
app <- function(input, output, session, .data) {
data.subset <- callModule(filters, "filter", .data = .data)
callModule(dataTable, "data.table", .data = data.subset) ## remove parantheses here
}
ui <- fluidPage(
appUI("app")
)
server <- function(input, output, session) {
callModule(app, "app", .data = iris)
}
shinyApp(ui, server)
总而言之,这是 Joe Cheng 对 similar issue
的引用
Hi Mark, the code in linkedScatter itself is correct; but when calling callModule, you want to pass the reactive itself by name (car_data) without reading it (car_data()).
callModule(linkedScatter, "scatters", car_data)
This is similar to how you can pass a function by name to something like lapply:
lapply(letters, toupper) # works
lapply(letters, toupper()) # doesn't work
因为我的 shiny
应用程序变得相当大,所以我最近将一些代码放入模块中(也是为了在不同的地方多次重用代码)。不知何故,部分代码不再按预期工作。
在这个例子中,我有一个根据输入元素和 return 反应式 data.frame
过滤数据的模块。在 mainPanel 中,我有一个模块,它根据过滤后的数据创建一个 dataTable
。但是反应性不起作用,当我更改 selectInput
时,dataTable
不会更新。
library(shiny)
library(DT)
filtersUI <- function(id) {
ns <- NS(id)
selectizeInput(
ns("Species"), label = "Species",
choices = levels(iris$Species),
selected = "virginica"
)
}
filters <- function(input, output, session, .data) {
inputs <- reactive({
list("Species" = input[["Species"]])
})
reactive({
.data[.data$Species %in% inputs()[["Species"]], ]
})
}
dataTableUI <- function(id) {
ns <- NS(id)
DT::dataTableOutput(ns("data.table"))
}
dataTable <- function(input, output, session, .data) {
output$data.table <- DT::renderDataTable({
DT::datatable(.data)
})
}
appUI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
filtersUI(ns("filter"))
),
mainPanel(
dataTableUI(ns("data.table"))
)
)
}
app <- function(input, output, session, .data) {
data.subset <- callModule(filters, "filter", .data = .data)
callModule(dataTable, "data.table", .data = data.subset())
}
ui <- fluidPage(
appUI("app")
)
server <- function(input, output, session) {
callModule(app, "app", .data = iris)
}
shinyApp(ui, server)
但是当将代码从子模块复制到 app
模块时,代码工作正常:
library(shiny)
library(DT)
appUI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
selectizeInput(
ns("Species"), label = "Species",
choices = levels(iris$Species),
selected = "virginica"
)
),
mainPanel(
DT::dataTableOutput(ns("data.table"))
)
)
}
app <- function(input, output, session, .data) {
inputs <- reactive({
list("Species" = input[["Species"]])
})
data.subset <- reactive({
.data[.data$Species %in% inputs()[["Species"]], ]
})
output$data.table <- DT::renderDataTable({
DT::datatable(data.subset())
})
}
ui <- fluidPage(
appUI("app")
)
server <- function(input, output, session) {
callModule(app, "app", .data = iris)
}
shinyApp(ui, server)
我知道在这个简单的示例中,模块化结构看起来有点矫枉过正,但在我的真实应用程序中,我的模块中有很多代码,我删除了这些代码以使这个示例最小化。因此,最好有一个使用与第一个代码片段中相同的模块化结构的解决方案。知道为什么会失败吗?
你用子模块创建了一个可重现的例子,做得非常好。然而,这个问题实际上与子模块没有任何关系。您只需要以不同方式传递反应对象 data.subset
即可。而不是
callModule(dataTable, "data.table", .data = data.subset())
你应该使用
callModule(dataTable, "data.table", .data = data.subset)
传递反应本身而不是它的当前值。然后可以像这样
在DT::renderDataTable
中“解析”该值
output$data.table <- DT::renderDataTable({
DT::datatable({.data()})
})
按照你的编码方式,“构建时”的数据,即未过滤的数据集被发送到模块,并且无法在途中观察到。
需要说明的是:注释行(## remove parantheses here
和 ## add parantheses here
)是我从您的原始代码中更改的唯一行。
library(shiny)
library(DT)
filtersUI <- function(id) {
ns <- NS(id)
selectizeInput(
ns("Species"), label = "Species",
choices = levels(iris$Species),
selected = "virginica"
)
}
filters <- function(input, output, session, .data) {
inputs <- reactive({
list("Species" = input[["Species"]])
})
reactive({
.data[.data$Species %in% inputs()[["Species"]], ]
})
}
dataTableUI <- function(id) {
ns <- NS(id)
DT::dataTableOutput(ns("data.table"))
}
dataTable <- function(input, output, session, .data) {
output$data.table <- DT::renderDataTable({
DT::datatable({.data()}) ## add parantheses here
})
}
appUI <- function(id) {
ns <- NS(id)
sidebarLayout(
sidebarPanel(
filtersUI(ns("filter"))
),
mainPanel(
dataTableUI(ns("data.table"))
)
)
}
app <- function(input, output, session, .data) {
data.subset <- callModule(filters, "filter", .data = .data)
callModule(dataTable, "data.table", .data = data.subset) ## remove parantheses here
}
ui <- fluidPage(
appUI("app")
)
server <- function(input, output, session) {
callModule(app, "app", .data = iris)
}
shinyApp(ui, server)
总而言之,这是 Joe Cheng 对 similar issue
的引用Hi Mark, the code in linkedScatter itself is correct; but when calling callModule, you want to pass the reactive itself by name (car_data) without reading it (car_data()).
callModule(linkedScatter, "scatters", car_data)
This is similar to how you can pass a function by name to something like lapply:
lapply(letters, toupper) # works
lapply(letters, toupper()) # doesn't work