Shiny-Module zur Codewiederverwendung
Kapseln Sie UI- und Serverlogik in namespaced, wiederverwendbaren Modulen
Shiny-Module zur Codewiederverwendung ist eine kostenlose R Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des R Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der R Academy-Kurs umfasst insgesamt 4 Lektionen.
Warum Shiny-Module?
Wenn Shiny-Apps wachsen, wird es unübersichtlich, den gesamten UI- und Server-Code in einer Datei zu verwalten. Module sind eigenständige Einheiten aus Shiny-UI und Serverlogik mit Namensräumen für IDs. Sie können dasselbe Modul mehrmals in einer App wiederverwenden, ohne ID-Konflikte zu verursachen, und Module unabhängig testen.
# Problem: without modules, ID conflicts arise
# ui <- fluidPage(
# selectInput('dataset', ...), # used by plot1 AND plot2!
# selectInput('dataset', ...) # duplicate ID — BROKEN
# )
# With modules: each instance has its own namespaced IDs
# plotModule('plot1', ...) -> input$plot1-dataset
# plotModule('plot2', ...) -> input$plot2-datasetNS() — Die Namespace-Funktion
Jede UI-Funktion eines Moduls beginnt mit ns <- NS(id). Alle an Shiny übergebenen IDs von UI-Elementen müssen in ns() eingeschlossen werden. Dadurch wird jeder ID id- vorangestellt und ein Namespace erstellt, der Konflikte zwischen Modulinstanzen verhindert.
# Module UI function
filter_plot_ui <- function(id) {
ns <- NS(id) # create the namespace function
tagList(
selectInput(ns('dataset'), 'Choose Dataset:',
choices = c('mtcars', 'iris', 'airquality')),
sliderInput(ns('n_rows'), 'Rows to show:', 1, 50, 20),
plotOutput(ns('scatter_plot'))
)
}moduleServer() — Die Serverfunktion
moduleServer(id, function(input, output, session) {...}) ist die moderne (seit Shiny 1.5) Methode, um die Serverlogik eines Moduls zu definieren. Innerhalb der Funktion erhalten input, output und session automatisch den passenden Namespace — Sie greifen auf input$dataset zu, nicht auf input$plot1-dataset.
# Module server function
filter_plot_server <- function(id) {
moduleServer(id, function(input, output, session) {
data <- reactive({
# input$dataset is already namespaced to this instance
head(get(input$dataset), input$n_rows)
})
output$scatter_plot <- renderPlot({
df <- data()
plot(df[[1]], df[[2]],
xlab = names(df)[1], ylab = names(df)[2])
})
})
}Module in der App verwenden
Rufen Sie die UI-Funktion des Moduls in ui und die Serverfunktion des Moduls in server auf, jeweils mit demselben id-String. Sie können dasselbe Modul mehrmals mit unterschiedlichen IDs aufrufen, um unabhängige Instanzen zu erstellen.
# Main app using the module twice
ui <- fluidPage(
h2('Plot 1'),
filter_plot_ui('plot1'), # instance 1
hr(),
h2('Plot 2'),
filter_plot_ui('plot2') # instance 2 — no ID conflicts!
)
server <- function(input, output, session) {
filter_plot_server('plot1') # wire up instance 1
filter_plot_server('plot2') # wire up instance 2
}
shinyApp(ui, server)Parameter an die Modul-UI übergeben
UI-Funktionen von Modulen sind ganz normale R-Funktionen. Fügen Sie über id hinaus zusätzliche Parameter hinzu, um das Erscheinungsbild oder Verhalten jeder Instanz bereits bei ihrer Erstellung anzupassen. Diese Parameter werden einmalig beim Aufbau der UI ausgewertet.
# Module UI with extra parameters
summary_table_ui <- function(id, title = 'Summary', height = '300px') {
ns <- NS(id)
tagList(
h4(title),
div(
style = paste0('height:', height, '; overflow-y: auto;'),
DTOutput(ns('tbl'))
)
)
}
# Use with custom titles
summary_table_ui('train_tbl', title = 'Training Data', height = '400px')
summary_table_ui('test_tbl', title = 'Test Data', height = '200px')Reaktive Werte IN ein Modul übergeben
Serverfunktionen von Modulen können reaktive Werte oder Ausdrücke als Parameter akzeptieren. Dadurch kann die übergeordnete App Daten an untergeordnete Module weitergeben. Rufen Sie die Reaktivität innerhalb des Moduls wie eine Funktion auf, um ihren aktuellen Wert abzurufen.
# Module that accepts a reactive as input
chart_module_server <- function(id, data_reactive) {
moduleServer(id, function(input, output, session) {
output$chart <- renderPlot({
df <- data_reactive() # call the reactive passed in
ggplot2::ggplot(df, ggplot2::aes(x = x, y = y)) +
ggplot2::geom_point(colour = input$colour)
})
})
}
# In main server:
server <- function(input, output, session) {
shared_data <- reactive({ load_data(input$source) })
chart_module_server('chart1', data_reactive = shared_data)
chart_module_server('chart2', data_reactive = shared_data)
}Reaktive Werte AUS einem Modul zurückgeben
Serverfunktionen von Modulen können reaktive Werte an die übergeordnete App zurückgeben. So können untergeordnete Module Informationen nach oben weitergeben. Geben Sie aus moduleServer() einen reaktiven Wert oder eine Liste reaktiver Werte zurück und speichern Sie das Ergebnis in der übergeordneten Serverfunktion.
# Module that returns a reactive to the parent
filter_module_server <- function(id, all_data) {
moduleServer(id, function(input, output, session) {
# Return the filtered data reactive
filtered <- reactive({
all_data[all_data$group == input$group_filter, ]
})
return(filtered) # parent can use this reactive
})
}
# In main server:
server <- function(input, output, session) {
raw_data <- reactive({ read.csv('data.csv') })
# filtered_data is a reactive returned from the module
filtered_data <- filter_module_server('filter1', raw_data)
output$main_plot <- renderPlot({ plot(filtered_data()) })
}Organisation von Moduldateien
Platzieren Sie bei großen Apps jedes Modul in einer eigenen Datei im Ordner R/. Shiny bindet beim Laden der App automatisch alle Dateien in R/ ein. Dadurch bleibt jedes Modul eigenständig und kann unabhängig mit dem Paket shinytest2 getestet werden.
# Recommended project structure:
# myapp/
# app.R # main app: source modules + wire up
# R/
# mod_filter_plot.R # filter_plot_ui() + filter_plot_server()
# mod_summary_table.R # summary_table_ui() + summary_table_server()
# mod_download.R # download_ui() + download_server()
# tests/
# testthat/test-mod_filter_plot.R
# In app.R:
library(shiny)
# source('R/mod_filter_plot.R') # not needed if in R/ folder
ui <- fluidPage(filter_plot_ui('p1'))
server <- function(input, output, session) { filter_plot_server('p1') }
shinyApp(ui, server)Verschachtelte Module
Module können andere Module enthalten. Das übergeordnete Modul gibt seinen eigenen session-Namespace über das Argument session an Aufrufe untergeordneter Module weiter. Jede Verschachtelungsebene fügt ein weiteres Namespace-Präfix hinzu: outer-inner-element_id.
# Outer module uses an inner module
outer_server <- function(id) {
moduleServer(id, function(input, output, session) {
# Call an inner module using this module's session
inner_result <- inner_module_server(
id = 'inner',
session = session # passes the namespaced session
)
output$combined <- renderText({
paste('Inner result:', inner_result())
})
})
}
# ID chain: outer -> inner
# Full ID: outer-inner-elementModule mit shinytest2 testen
Mit dem Paket shinytest2 können Sie automatisierte Tests für Module schreiben, indem Sie diese in eine minimale App einbetten. Verwenden Sie AppDriver, um den Browser zu steuern, Eingaben festzulegen und Ausgabewerte zu überprüfen — und das alles ohne eine echte Browsersitzung.
library(shinytest2)
# Wrap the module in a testable app
test_that('filter_plot module filters correctly', {
test_app <- shinyApp(
ui = fluidPage(filter_plot_ui('test')),
server = function(input, output, session) {
filter_plot_server('test')
}
)
app <- AppDriver$new(test_app)
app$set_inputs('test-dataset' = 'iris') # namespaced input
app$wait_for_idle()
# Assert plot was rendered
expect_true(!is.null(app$get_value(output = 'test-scatter_plot')))
})Muster für die Kommunikation zwischen Modulen
Zusammenfassung der Kommunikationsmuster zwischen Modulen und der übergeordneten App:
- Übergeordnete App an Modul: Reaktivität als Parameter an den Modulserver übergeben.
- Modul an übergeordnete App: Reaktivität aus moduleServer() zurückgeben.
- Zwischen Geschwistermodulen: Die übergeordnete App verwaltet den gemeinsamen Zustand (
reactiveValues) und übergibt ihn an jedes Modul. - Globaler Zustand:
reactiveValuesin der übergeordneten App definieren und Referenzen nach unten weitergeben.
# Sibling module communication via parent state
server <- function(input, output, session) {
shared <- reactiveValues(selected_row = NULL)
# Table module sets the selection
table_module_server('tbl', shared_state = shared)
# Detail module reads the selection
detail_module_server('detail', shared_state = shared)
# Both modules communicate through 'shared' reactiveValues
# Table sets shared$selected_row; Detail reads it
}Kurztest
Warum müssen alle IDs von UI-Elementen in einer Modul-UI-Funktion in ns() eingeschlossen werden?
Rückblick auf Shiny-Module
Die wichtigsten Erkenntnisse aus „Shiny Modules for Code Reuse“:
- Module verhindern ID-Konflikte, indem sie alle IDs mit
NS(id)in Namespaces einordnen. - Modul-UI: normale Funktion mit
ns <- NS(id); alle IDs mitns()einschließen. - Modulserver:
moduleServer(id, function(input, output, session) {...}). - Reaktive Werte als Funktionsparameter IN Module übergeben; reaktive Werte für die Kommunikation nach oben ZURÜCKGEBEN.
- Dasselbe Modul mehrmals mit unterschiedlichen IDs für unabhängige Instanzen aufrufen.
- Module in Dateien wie
R/mod_*.Rorganisieren; Shiny bindet den OrdnerR/automatisch ein. - Module mit
shinytest2::AppDrivertesten.
# Complete module example
my_module_ui <- function(id) {
ns <- NS(id)
tagList(selectInput(ns('var'), 'Variable:', choices = names(mtcars)),
plotOutput(ns('hist')))
}
my_module_server <- function(id, data) {
moduleServer(id, function(input, output, session) {
output$hist <- renderPlot(hist(data()[[input$var]]))
})
}
# Use it:
ui <- fluidPage(my_module_ui('m1'), my_module_ui('m2'))
server <- function(input, output, session) {
d <- reactive(mtcars)
my_module_server('m1', d)
my_module_server('m2', d)
}
shinyApp(ui, server)Häufig gestellte Fragen
Ist die Lektion „Shiny-Module zur Codewiederverwendung“ kostenlos?
Ja — der vollständige Text von „Shiny-Module zur Codewiederverwendung“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des R Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der R Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Shiny-Module zur Codewiederverwendung“?
Kapseln Sie UI- und Serverlogik in namespaced, wiederverwendbaren Modulen Du übst R Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um R Academy zu starten?
Keine Vorkenntnisse erforderlich. R Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.
Wie lange dauert die Lektion „Shiny-Module zur Codewiederverwendung“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser R Academy-Lektion Code schreiben und ausführen?
Ja. Jede R Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Reaktive Programmierung im Detail
- Shiny-Module zur Codewiederverwendung
- Dynamische UI mit renderUI und insertUI
- Shiny-Apps bereitstellen