Shiny-moduler til genbrug af kode
Indkapsl UI- og serverlogik i namespacede moduler, der kan genbruges.
Shiny-moduler til genbrug af kode er en gratis R Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i R Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. R Academy-kurset indeholder 4 lektioner i alt.
Hvorfor Shiny-moduler?
Efterhånden som Shiny-apps vokser, bliver det uhåndterligt at have al UI- og serverkode i én fil. Moduler er selvstændige dele af Shiny-UI og serverlogik med navnerumsafgrænsede ID'er. Du kan genbruge det samme modul flere gange i én app uden konflikter mellem ID'er og teste moduler uafhængigt.
# 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() — Navnerumsfunktionen
Alle UI-funktioner til moduler begynder med ns <- NS(id). Alle ID'er for UI-elementer, der sendes til Shiny, skal pakkes ind i ns(). Det føjer id- til hvert ID og opretter et navnerum, der forhindrer konflikter mellem modulinstanser.
# 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() — Serverfunktionen
moduleServer(id, function(input, output, session) {...}) er den moderne måde (Shiny 1.5+) at definere serverlogik for moduler på. Inde i funktionen får input, output og session automatisk navnerum — du tilgår input$dataset, ikke 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])
})
})
}Brug af moduler i appen
Kald modulets UI-funktion i ui og modulets serverfunktion i server, begge med den samme id-streng. Du kan kalde det samme modul flere gange med forskellige ID'er for at oprette uafhængige instanser.
# 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)Overførsel af parametre til modul-UI
UI-funktioner til moduler er blot almindelige R-funktioner. Tilføj ekstra parametre ud over id for at tilpasse hver instans' udseende eller adfærd, når den oprettes. De evalueres én gang, når UI'en bygges.
# 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')Overførsel af reaktive værdier TIL et modul
Serverfunktioner for moduler kan modtage reaktive værdier eller udtryk som parametre. Det gør det muligt for den overordnede app at sende data ned til undermoduler. Inde i modulet kalder du den reaktive værdi som en funktion for at hente dens aktuelle værdi.
# 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)
}Returnering af reaktive værdier FRA et modul
Serverfunktioner for moduler kan returnere reaktive værdier til den overordnede app. Det gør det muligt for undermoduler at kommunikere opad. Returnér en reaktiv værdi eller en liste med reaktive værdier fra moduleServer(), og gem den i den overordnede 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()) })
}Organisering af modulfiler
I store apps skal du placere hvert modul i sin egen fil i en R/-mappe. Shiny indlæser automatisk kildekoden fra alle filer i R/, når appen indlæses. Det holder hvert modul selvstændigt og gør det muligt at teste det uafhængigt ved hjælp af pakken shinytest2.
# 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)Indlejrede moduler
Moduler kan indeholde andre moduler. Forældremodulet sender sit eget session-navnerum videre til kald af undermoduler ved hjælp af argumentet session. Hvert niveau af indlejring tilføjer endnu et navnerumspræfiks: 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-elementTest af moduler med shinytest2
Pakken shinytest2 lader dig skrive automatiserede tests af moduler ved at pakke dem ind i en minimal app. Brug AppDriver til at styre browseren, angive input og kontrollere outputværdier — alt sammen uden en rigtig browsersession.
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')))
})Mønstre for modulkommunikation
En opsummering af kommunikationsmønstre mellem moduler og den overordnede app:
- Forælder til modul: send en reaktiv værdi som parameter til modulets serverfunktion.
- Modul til forælder: returnér en reaktiv værdi fra moduleServer().
- Mellem søskendemoduler: forælderen opbevarer delt tilstand (
reactiveValues) og sender den til hvert modul. - Global tilstand: brug
reactiveValues, der er defineret i forælderen, og send referencer ned.
# 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
}Hurtigt tjek
Hvorfor skal alle ID'er for UI-elementer i en UI-funktion til et modul pakkes ind i ns()?
Opsummering af Shiny-moduler
Vigtigste pointer fra Shiny-moduler til genbrug af kode:
- Moduler forhindrer konflikter mellem ID'er ved at give alle ID'er et navnerum med
NS(id). - Modul-UI: almindelig funktion med
ns <- NS(id); pak alle ID'er ind ins(). - Modulserver:
moduleServer(id, function(input, output, session) {...}). - Send reaktive værdier TIL moduler som funktionsparametre; RETURNÉR reaktive værdier for kommunikation opad.
- Kald det samme modul flere gange med forskellige ID'er for at oprette uafhængige instanser.
- Organiser moduler i filer af typen
R/mod_*.R; Shiny indlæser automatisk kildekoden fra mappenR/. - Test moduler med
shinytest2::AppDriver.
# 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)Lær R med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 43
- Lektioner
- 159
Ofte stillede spørgsmål
Er lektionen “Shiny-moduler til genbrug af kode” gratis?
Ja — alle 3 lektioner i læringssporet R Academy, inklusive “Shiny-moduler til genbrug af kode”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. R Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Shiny-moduler til genbrug af kode”?
Indkapsl UI- og serverlogik i namespacede moduler, der kan genbruges. Du øver dig i R Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på R Academy?
Der kræves ingen tidligere erfaring. R Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “Shiny-moduler til genbrug af kode”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne R Academy-lektion?
Ja. Alle R Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Fordybelse i reaktiv programmering
- Shiny-moduler til genbrug af kode
- Dynamisk UI med renderUI og insertUI
- Udrulning af Shiny-apps