build-shiny-module
关于
This skill helps developers create reusable Shiny modules with proper namespace isolation using NS(). It covers building UI/server pairs, handling reactive values, and enabling module communication and nesting. Use it when extracting reusable components from growing apps or encapsulating complex logic behind clean interfaces.
快速安装
Claude Code
推荐npx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/build-shiny-module在 Claude Code 中复制并粘贴此命令以安装该技能
技能文档
Build Shiny Module
創具正命名空間隔離、反應式溝通、可組合之可復用 Shiny UI/server 模組對。
適用時機
- 自漸長之 Shiny 應用抽可復用組件
- 建多處可用之 UI 小部件
- 以潔介面封繁之反應邏輯
- 自更小可測之單位組大應用
輸入
- 必要:模組之旨與功能描
- 必要:輸入/輸出之合同(模組受何返何)
- 選擇性:模組是否嵌他模組(預設:否)
- 選擇性:框架脈絡(golem、rhino、或 vanilla)
步驟
步驟一:定模組介面
書代碼前,定模組所受所返:
Module: data_filter
Inputs: reactive dataset, column names to filter on
Outputs: reactive filtered dataset
UI: filter controls (selectInput, sliderInput, dateRangeInput)
預期: 明之合同,指定反應輸入、反應輸出、UI 元素。
失敗時: 若介面不明,模組恐過廣。分為單責之小模組。
步驟二:創模組 UI 函
#' Data Filter Module UI
#'
#' @param id Module namespace ID
#' @return A tagList of filter controls
#' @export
dataFilterUI <- function(id) {
ns <- NS(id)
tagList(
selectInput(
ns("column"),
"Filter column",
choices = NULL
),
uiOutput(ns("filter_control")),
actionButton(ns("apply"), "Apply Filter", class = "btn-primary")
)
}
要則:
- 函名循
<name>UI約定 - 首參恆為
id - 於頂立
ns <- NS(id) - 每
inputId與outputId皆包於ns() - 返
tagList()以允彈性置
預期: UI 函創命名空間之輸入/輸出元素。
失敗時: 若用模組二次時 ID 碰撞,查每 ID 皆包於 ns()。常漏:renderUI() 或 uiOutput() 內之 ID——此亦需 ns()。
步驟三:創模組伺服器函
#' Data Filter Module Server
#'
#' @param id Module namespace ID
#' @param data Reactive expression returning a data frame
#' @param columns Character vector of filterable column names
#' @return Reactive expression returning the filtered data frame
#' @export
dataFilterServer <- function(id, data, columns) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
# Update column choices when data changes
observeEvent(data(), {
available <- intersect(columns, names(data()))
updateSelectInput(session, "column", choices = available)
})
# Dynamic filter control based on selected column
output$filter_control <- renderUI({
req(input$column)
col_data <- data()[[input$column]]
if (is.numeric(col_data)) {
sliderInput(
ns("value_range"),
"Range",
min = min(col_data, na.rm = TRUE),
max = max(col_data, na.rm = TRUE),
value = range(col_data, na.rm = TRUE)
)
} else {
selectInput(
ns("value_select"),
"Values",
choices = unique(col_data),
multiple = TRUE,
selected = unique(col_data)
)
}
})
# Return filtered data as a reactive
filtered <- eventReactive(input$apply, {
req(input$column)
col <- input$column
df <- data()
if (is.numeric(df[[col]])) {
req(input$value_range)
df[df[[col]] >= input$value_range[1] &
df[[col]] <= input$value_range[2], ]
} else {
req(input$value_select)
df[df[[col]] %in% input$value_select, ]
}
}, ignoreNULL = FALSE)
return(filtered)
})
}
要則:
- 函名循
<name>Server約定 - 首參恆為
id - 餘參為反應式表達或靜值
- 用
moduleServer(id, function(input, output, session) { ... }) - 伺服器內創之動態 UI 用
session$ns - 明返反應值
預期: 伺服器函處輸入、返反應輸出。
失敗時: 若反應值不更,查動態 UI 之輸入用 session$ns(非外之 ns)。若模組返 NULL,確 return() 為 moduleServer() 內末表達。
步驟四:於父應用接模組
# In app_ui.R or ui
ui <- page_sidebar(
title = "Analysis App",
sidebar = sidebar(
dataFilterUI("filter1")
),
card(
DT::dataTableOutput("table")
)
)
# In app_server.R or server
server <- function(input, output, session) {
# Raw data source
raw_data <- reactive({ mtcars })
# Call module — capture its return value
filtered_data <- dataFilterServer(
"filter1",
data = raw_data,
columns = c("cyl", "mpg", "hp", "wt")
)
# Use the module's returned reactive
output$table <- DT::renderDataTable({
filtered_data()
})
}
預期: 模組於 UI 顯,其所返反應流至下游輸出。
失敗時: 若模組 UI 不渲,驗 UI 與伺服器呼間 id 字串相符。若所返反應為 NULL,查伺服器函實返值。
步驟五:組嵌模組(選)
含他模組之模組:
analysisUI <- function(id) {
ns <- NS(id)
tagList(
dataFilterUI(ns("filter")),
plotOutput(ns("plot"))
)
}
analysisServer <- function(id, data) {
moduleServer(id, function(input, output, session) {
# Call inner module with namespaced ID
filtered <- dataFilterServer("filter", data = data, columns = names(data()))
output$plot <- renderPlot({
req(filtered())
plot(filtered())
})
return(filtered)
})
}
要則:UI 中以 ns("inner_id") 嵌。伺服器中僅以 "inner_id" 呼——moduleServer 處命名空間之鏈。
預期: 內模組於外模組之命名空間中正渲。
失敗時: 若內模組 UI 不現,恐忘於外 UI 函中以 ns() 包內模組 ID。若伺服器溝通斷,查內模組 ID 相符(伺服器呼中無 ns())。
步驟六:隔離測模組
# Quick test app for the module
if (interactive()) {
shiny::shinyApp(
ui = fluidPage(
dataFilterUI("test"),
DT::dataTableOutput("result")
),
server = function(input, output, session) {
data <- reactive(iris)
filtered <- dataFilterServer("test", data, names(iris))
output$result <- DT::renderDataTable(filtered())
}
)
}
預期: 模組於最小測應用中正工。
失敗時: 若模組於隔離敗而於全應用工(或反),查對全域變或父會話狀態之隱依。
驗證
- 模組 UI 函受
id為首參且用NS(id) - UI 中每輸入/輸出 ID 皆包於
ns() - 模組伺服器用
moduleServer(id, function(input, output, session) { ... }) - 伺服器中動態 UI 之 ID 用
session$ns - 模組可實例化多次而無 ID 碰撞
- 反應返值可為父應用存取
- 模組於最小獨立測應用中工
常見陷阱
renderUI()中忘ns():伺服器內創之動態 UI 須用session$ns——外之ns於moduleServer()內不可得- 傳非反應數據:隨時變之模組參須為反應式表達。傳
reactive(data)非data - ID 失配:UI 呼中之
id字串須與伺服器呼中之id完相符 - 不返反應:若模組算父所需之物,須
return()一反應。忘此為默錯 - 嵌模組之命名空間:UI:
ns("inner_id")。伺服器:僅"inner_id"。混致雙包或漏前綴
相關技能
scaffold-shiny-app— 加模組前立應用結構test-shiny-app— 以 testServer() 單元測測模組design-shiny-ui— 模組 UI 之 bslib 版型與主題optimize-shiny-performance— 模組內之快取與 async 模式
GitHub 仓库
相关推荐技能
content-collections
元Content Collections 是一个 TypeScript 优先的构建工具,可将本地 Markdown/MDX 文件转换为类型安全的数据集合。它专为构建博客、文档站和内容密集型 Vite+React 应用而设计,提供基于 Zod 的自动模式验证。该工具涵盖从 Vite 插件配置、MDX 编译到生产环境部署的完整工作流。
polymarket
元这个Claude Skill为开发者提供完整的Polymarket预测市场开发支持,涵盖API调用、交易执行和市场数据分析。关键特性包括实时WebSocket数据流,可监控实时交易、订单和市场动态。开发者可用它构建预测市场应用、实施交易策略并集成实时市场预测功能。
creating-opencode-plugins
元该Skill帮助开发者创建OpenCode插件,用于接入命令、文件、LSP等25+种事件。它提供了插件结构、事件API规范和JavaScript/TypeScript实现模式,适合需要拦截操作、扩展功能或自定义事件处理的场景。开发者可通过它快速构建响应式模块来增强OpenCode AI助手的能力。
sglang
元SGLang是一个专为LLM设计的高性能推理框架,特别适用于需要结构化输出的场景。它通过RadixAttention前缀缓存技术,在处理JSON、正则表达式、工具调用等具有重复前缀的复杂工作流时,能实现极速生成。如果你正在构建智能体或多轮对话系统,并追求远超vLLM的推理性能,SGLang是理想选择。
