Módulos de Shiny para reutilizar código
Encapsule la lógica de la interfaz de usuario y del servidor en módulos reutilizables con espacios de nombres.
Módulos de Shiny para reutilizar código es una lección gratuita de R Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de R Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de R Academy incluye 4 lecciones en total.
¿Por qué usar módulos de Shiny?
A medida que crecen las aplicaciones de Shiny, resulta inmanejable tener todo el código de la interfaz de usuario y del servidor en un solo archivo. Los módulos son componentes independientes de la interfaz y la lógica del servidor de Shiny, con identificadores en un espacio de nombres. Puede reutilizar el mismo módulo varias veces en una aplicación sin conflictos entre identificadores y probar los módulos de forma independiente.
# 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() — La función de espacio de nombres
Toda función de interfaz de un módulo comienza con ns <- NS(id). Todos los identificadores de elementos de la interfaz que se pasen a Shiny deben envolverse en ns(). Esto antepone id- a cada identificador, creando un espacio de nombres que evita conflictos entre instancias del módulo.
# 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() — La función del servidor
moduleServer(id, function(input, output, session) {...}) es la forma moderna (Shiny 1.5+) de definir la lógica del servidor de un módulo. Dentro de la función, input, output y session reciben automáticamente el espacio de nombres; se accede a input$dataset, no a 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])
})
})
}Usar módulos en la aplicación
Llame a la función de interfaz del módulo en ui y a la función del servidor del módulo en server, utilizando en ambos casos la misma cadena id. Puede llamar varias veces al mismo módulo con identificadores diferentes para crear instancias independientes.
# 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)Pasar parámetros a la interfaz del módulo
Las funciones de interfaz de los módulos son simplemente funciones habituales de R. Añada parámetros adicionales, además de id, para personalizar la apariencia o el comportamiento de cada instancia en el momento de crearla. Estos parámetros se evalúan una vez, cuando se construye la interfaz.
# 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')Pasar valores reactivos A un módulo
Las funciones del servidor de los módulos pueden aceptar valores o expresiones reactivas como parámetros. Esto permite que la aplicación principal pase datos a los módulos secundarios. Dentro del módulo, llame al valor reactivo como si fuera una función para obtener su valor actual.
# 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)
}Devolver valores reactivos DESDE un módulo
Las funciones del servidor de los módulos pueden devolver valores reactivos a la aplicación principal. Esto permite que los módulos secundarios se comuniquen con ella. Devuelva un valor reactivo o una lista de valores reactivos desde moduleServer() y asígnelo en la función del servidor principal.
# 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()) })
}Organización de los archivos de los módulos
En aplicaciones grandes, coloque cada módulo en su propio archivo dentro de una carpeta R/. Shiny carga automáticamente todos los archivos de R/ cuando se inicia la aplicación. Así, cada módulo se mantiene independiente y puede probarse por separado mediante el paquete 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)Módulos anidados
Los módulos pueden contener otros módulos. El módulo principal pasa su propio espacio de nombres de session a las llamadas de los módulos secundarios mediante el argumento session. Cada nivel de anidamiento añade otro prefijo de espacio de nombres: 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-elementProbar módulos con shinytest2
El paquete shinytest2 permite escribir pruebas automatizadas para módulos envolviéndolos en una aplicación mínima. Use AppDriver para controlar el navegador, establecer entradas y comprobar valores de salida, todo ello sin una sesión de navegador real.
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')))
})Patrones de comunicación entre módulos
Resumen de los patrones de comunicación entre los módulos y la aplicación principal:
- De la aplicación principal al módulo: pasar un valor reactivo como parámetro al servidor del módulo.
- Del módulo a la aplicación principal: devolver un valor reactivo desde moduleServer().
- Entre módulos hermanos: la aplicación principal mantiene el estado compartido (
reactiveValues) y lo pasa a cada módulo. - Estado global: usar
reactiveValuesdefinido en la aplicación principal y pasar referencias a los módulos.
# 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
}Comprobación rápida
¿Por qué deben envolverse con ns() todos los identificadores de elementos de la interfaz en una función de interfaz de módulo?
Repaso de los módulos de Shiny
Conceptos clave de Módulos de Shiny para reutilizar código:
- Los módulos evitan conflictos entre identificadores al asignar todos los identificadores a un espacio de nombres con
NS(id). - Interfaz del módulo: función habitual con
ns <- NS(id); envuelva todos los identificadores conns(). - Servidor del módulo:
moduleServer(id, function(input, output, session) {...}). - Pase valores reactivos A los módulos como parámetros de función y DEVUELVA valores reactivos para la comunicación ascendente.
- Llame varias veces al mismo módulo con identificadores diferentes para crear instancias independientes.
- Organice los módulos en archivos
R/mod_*.R; Shiny carga automáticamente la carpetaR/. - Pruebe los módulos con
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)Preguntas frecuentes
¿La lección «Módulos de Shiny para reutilizar código» es gratis?
Sí — el texto completo de «Módulos de Shiny para reutilizar código» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de R Academy, actualiza a CoddyKit PRO. El curso de R Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Módulos de Shiny para reutilizar código»?
Encapsule la lógica de la interfaz de usuario y del servidor en módulos reutilizables con espacios de nombres. Practicas R Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar R Academy?
No se requiere experiencia previa. R Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Módulos de Shiny para reutilizar código»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de R Academy?
Sí. Cada lección de R Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Profundización en la programación reactiva
- Módulos de Shiny para reutilizar código
- Interfaz de usuario dinámica con renderUI e insertUI
- Implementación de aplicaciones Shiny