在一个Shiny应用中模块化响应式表达式

huangapple go评论110阅读模式
英文:

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 &lt;- function(region) {
numericInput(region$id, region$label, min = 0, max = 3, step = 1, value = 0)
}
# List of inputs
input_details &lt;- list(
list(id = &quot;A1&quot;, label = &quot;0 A&quot;),
list(id = &quot;B2&quot;, label = &quot;1 B&quot;),
list(id = &quot;C3&quot;, label = &quot;2 C&quot;)
)
# ui module
name_UI &lt;- function(id, numeric_inputs) {
ns &lt;- NS(id)
tagList(
tags$div(
id = &quot;inline&quot;,
fluidRow(
h4(&quot;ABC&quot;),
column(width = 6,
numeric_inputs,
numericInput(ns(&quot;ABC&quot;), &quot;ABC&quot;, min = 0, max = 39, step = 1, value = 0)
)
)
)
)
}
# server module
name_server &lt;- function(id, input_details) {
moduleServer(id, function(input, output, session) {
ns &lt;- session$ns
sum_reactive &lt;- reactive({
sum_values &lt;- 0
for(region in input_details) {
input_id &lt;- ns(region$id)
val &lt;- input[[input_id]]
if (!is.null(val)) {
sum_values &lt;- sum_values + as.numeric(val)
}
}
sum_values
})
# Update the value of ABC when the inputs change
observe({
updateNumericInput(session, ns(&quot;ABC&quot;), value = sum_reactive())
})
})
}
# UI
ui &lt;- fluidPage(
name_UI(&quot;mod_1&quot;, lapply(input_details, createNumericInput)) # Apply createNumericInput to input_details here
)
# Server logic
server &lt;- function(input, output, session) {
name_server(&quot;mod_1&quot;, input_details)
}
# Run the app
shinyApp(ui = ui, server = server)

答案1

得分: 2

以下是您的代码的问题:

  1. 在使用lapply创建数字输入时,您没有考虑到模块的命名空间,即您没有在模块命名空间中创建输入。为了解决这个问题,我在createNumericInput中添加了一个ns参数。然后,我们可以简单地使用例如input[[region$id]]来访问服务器中输入的值。使用ns(...)将不会产生效果,因为正如前面所说,您没有在模块的命名空间中创建输入。

  2. 在服务器部分,在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)

在一个Shiny应用中模块化响应式表达式

英文:

There are two issues with your code:

  1. 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 a ns argument to createNumericInput. And of course could we then simply use e.g. input[[region$id]] to access the value of the input in the server. Using ns(...) will have no effect as just said you have not created the inputs in the module's namespace.

  2. In the server part, in updateNumericInput you don't have to wrap the input id in ns() 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 &lt;- function(region, ns = NULL) {
id &lt;- 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 &lt;- list(
list(id = &quot;A1&quot;, label = &quot;0 A&quot;),
list(id = &quot;B2&quot;, label = &quot;1 B&quot;),
list(id = &quot;C3&quot;, label = &quot;2 C&quot;)
)
# ui module
name_UI &lt;- function(id, numeric_inputs) {
ns &lt;- NS(id)
tagList(
tags$div(
id = &quot;inline&quot;,
fluidRow(
h4(&quot;ABC&quot;),
column(
width = 6,
lapply(input_details, createNumericInput, ns = ns),
numericInput(ns(&quot;ABC&quot;), &quot;ABC&quot;, min = 0, max = 39, step = 1, value = 0)
)
)
)
)
}
# server module
name_server &lt;- function(id, input_details) {
moduleServer(id, function(input, output, session) {
ns &lt;- session$ns
sum_reactive &lt;- reactive({
sum_values &lt;- 0
for (region in input_details) {
val &lt;- input[[region$id]]
if (!is.null(val)) {
sum_values &lt;- sum_values + as.numeric(val)
}
}
sum_values
})
observe({
updateNumericInput(session, &quot;ABC&quot;, value = sum_reactive())
})
})
}
# UI
ui &lt;- fluidPage(
name_UI(&quot;mod_1&quot;, input_details) # Apply createNumericInput to input_details here
)
# Server logic
server &lt;- function(input, output, session) {
name_server(&quot;mod_1&quot;, input_details)
}
# Run the app
shinyApp(ui = ui, server = server)

在一个Shiny应用中模块化响应式表达式

huangapple
  • 本文由 发表于 2023年8月5日 15:51:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76840637.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定