英文:
Modularizing Reactive Expressions in a Shiny Application
问题
我目前正在重构我的Shiny应用程序,以利用模块来提高结构和可维护性。该应用程序涉及一系列数字输入字段,这些字段的值被求和并显示在标记为"ABC"的字段中。
尽管该应用程序在其最初的非模块化状态下正常运行,但在尝试将其重构为模块后,我遇到了一个与响应性有关的问题。具体而言,"ABC"字段应该反应性地显示所有输入字段的更新总和,但它没有反映出这些变化。
我怀疑我可能没有正确地在UI和服务器模块之间传递参数,但我不确定如何解决这个问题。我将感激任何帮助来改进和修正我的代码。谢谢!
library(shiny)
# 生成numericInputs的函数
createNumericInput <- function(region) {
numericInput(region$id, region$label, min = 0, max = 3, step = 1, value = 0)
}
# 输入列表
input_details <- list(
list(id = "A1", label = "0 A"),
list(id = "B2", label = "1 B"),
list(id = "C3", label = "2 C")
)
# UI模块
name_UI <- function(id, numeric_inputs) {
ns <- NS(id)
tagList(
tags$div(
id = "inline",
fluidRow(
h4("ABC"),
column(width = 6,
numeric_inputs,
numericInput(ns("ABC"), "ABC", min = 0, max = 39, step = 1, value = 0)
)
)
)
)
}
# 服务器模块
name_server <- function(id, input_details) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
sum_reactive <- reactive({
sum_values <- 0
for(region in input_details) {
input_id <- ns(region$id)
val <- input[[input_id]]
if (!is.null(val)) {
sum_values <- sum_values + as.numeric(val)
}
}
sum_values
})
# 当输入更改时更新ABC的值
observe({
updateNumericInput(session, ns("ABC"), value = sum_reactive())
})
})
}
# UI
ui <- fluidPage(
name_UI("mod_1", lapply(input_details, createNumericInput)) # 在这里应用createNumericInput到input_details
)
# 服务器逻辑
server <- function(input, output, session) {
name_server("mod_1", input_details)
}
# 运行应用程序
shinyApp(ui = ui, server = server)
英文:
I'm currently working on refactoring my Shiny application to make use of modules for better structure and maintainability. The application involves a series of numeric input fields, the values of which are summed and displayed in a field labeled "ABC".
While the app functions as expected in its initial non-modularized state, after attempting to refactor it into modules, I'm encountering an issue with reactivity. Specifically, the "ABC" field, which should display the updated sum of all input fields, is not reflecting the changes reactively.
I suspect that I may not be passing arguments correctly between the UI and server modules, but I'm not sure how to resolve this issue. I would appreciate any assistance to help improve and correct my code. Thank you!"
library(shiny)
# Function to generate numericInputs
createNumericInput <- function(region) {
numericInput(region$id, region$label, min = 0, max = 3, step = 1, value = 0)
}
# List of inputs
input_details <- list(
list(id = "A1", label = "0 A"),
list(id = "B2", label = "1 B"),
list(id = "C3", label = "2 C")
)
# ui module
name_UI <- function(id, numeric_inputs) {
ns <- NS(id)
tagList(
tags$div(
id = "inline",
fluidRow(
h4("ABC"),
column(width = 6,
numeric_inputs,
numericInput(ns("ABC"), "ABC", min = 0, max = 39, step = 1, value = 0)
)
)
)
)
}
# server module
name_server <- function(id, input_details) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
sum_reactive <- reactive({
sum_values <- 0
for(region in input_details) {
input_id <- ns(region$id)
val <- input[[input_id]]
if (!is.null(val)) {
sum_values <- sum_values + as.numeric(val)
}
}
sum_values
})
# Update the value of ABC when the inputs change
observe({
updateNumericInput(session, ns("ABC"), value = sum_reactive())
})
})
}
# UI
ui <- fluidPage(
name_UI("mod_1", lapply(input_details, createNumericInput)) # Apply createNumericInput to input_details here
)
# Server logic
server <- function(input, output, session) {
name_server("mod_1", input_details)
}
# Run the app
shinyApp(ui = ui, server = server)
答案1
得分: 2
以下是您的代码的问题:
-
在使用
lapply
创建数字输入时,您没有考虑到模块的命名空间,即您没有在模块命名空间中创建输入。为了解决这个问题,我在createNumericInput
中添加了一个ns
参数。然后,我们可以简单地使用例如input[[region$id]]
来访问服务器中输入的值。使用ns(...)
将不会产生效果,因为正如前面所说,您没有在模块的命名空间中创建输入。 -
在服务器部分,在
updateNumericInput
中,您不必使用ns()
包装输入的ID,或者正如Hadley在Mastering Shiny中所说:请注意,
moduleServer()
会自动处理命名空间。
注意:我将使用lapply
创建数字输入的操作移到了模块UI内,并只将input_details
列表传递给UI。
library(shiny)
# 生成numericInputs的函数
createNumericInput <- function(region, ns = NULL) {
id <- if (!is.null(ns)) ns(region$id) else region$id
numericInput(id, region$label, min = 0, max = 3, step = 1, value = 0)
}
# 输入列表
input_details <- list(
list(id = "A1", label = "0 A"),
list(id = "B2", label = "1 B"),
list(id = "C3", label = "2 C")
)
# 模块UI
name_UI <- function(id, numeric_inputs) {
ns <- NS(id)
tagList(
tags$div(
id = "inline",
fluidRow(
h4("ABC"),
column(
width = 6,
lapply(input_details, createNumericInput, ns = ns),
numericInput(ns("ABC"), "ABC", min = 0, max = 39, step = 1, value = 0)
)
)
)
)
}
# 模块服务器
name_server <- function(id, input_details) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
sum_reactive <- reactive({
sum_values <- 0
for (region in input_details) {
val <- input[[region$id]]
if (!is.null(val)) {
sum_values <- sum_values + as.numeric(val)
}
}
sum_values
})
observe({
updateNumericInput(session, "ABC", value = sum_reactive())
})
})
}
# UI
ui <- fluidPage(
name_UI("mod_1", input_details) # 在此处对input_details应用createNumericInput
)
# 服务器逻辑
server <- function(input, output, session) {
name_server("mod_1", input_details)
}
# 运行应用程序
shinyApp(ui = ui, server = server)
英文:
There are two issues with your code:
-
When creating your numeric inputs via
lapply
you did not take care of the module's namespace, i.e. you did not create the inputs in the module namespace. To fix that I added ans
argument tocreateNumericInput
. And of course could we then simply use e.g.input[[region$id]]
to access the value of the input in the server. Usingns(...)
will have no effect as just said you have not created the inputs in the module's namespace. -
In the server part, in
updateNumericInput
you don't have to wrap the input id inns()
or as Hadley has put it in Mastering Shiny> Note that
moduleServer()
takes care of the namespacing automatically.
Note: I moved the creation of the numeric inputs via lapply
inside the module UI and only pass the input_details
list to the UI.
library(shiny)
# Function to generate numericInputs
createNumericInput <- function(region, ns = NULL) {
id <- if (!is.null(ns)) ns(region$id) else region$id
numericInput(id, region$label, min = 0, max = 3, step = 1, value = 0)
}
# List of inputs
input_details <- list(
list(id = "A1", label = "0 A"),
list(id = "B2", label = "1 B"),
list(id = "C3", label = "2 C")
)
# ui module
name_UI <- function(id, numeric_inputs) {
ns <- NS(id)
tagList(
tags$div(
id = "inline",
fluidRow(
h4("ABC"),
column(
width = 6,
lapply(input_details, createNumericInput, ns = ns),
numericInput(ns("ABC"), "ABC", min = 0, max = 39, step = 1, value = 0)
)
)
)
)
}
# server module
name_server <- function(id, input_details) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
sum_reactive <- reactive({
sum_values <- 0
for (region in input_details) {
val <- input[[region$id]]
if (!is.null(val)) {
sum_values <- sum_values + as.numeric(val)
}
}
sum_values
})
observe({
updateNumericInput(session, "ABC", value = sum_reactive())
})
})
}
# UI
ui <- fluidPage(
name_UI("mod_1", input_details) # Apply createNumericInput to input_details here
)
# Server logic
server <- function(input, output, session) {
name_server("mod_1", input_details)
}
# Run the app
shinyApp(ui = ui, server = server)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论