Módulos Shiny para reutilização de código
Encapsule a lógica da interface e do servidor em módulos com espaço de nomes e reutilizáveis.
Módulos Shiny para reutilização de código é uma aula grátis de R Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de R Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de R Academy inclui 4 aulas no total.
Por que usar módulos Shiny?
À medida que os aplicativos Shiny crescem, manter todo o código da interface e do servidor em um único arquivo se torna difícil de gerenciar. Módulos são partes autocontidas da interface e da lógica do servidor Shiny, com IDs em um espaço de nomes. Você pode reutilizar o mesmo módulo várias vezes em um aplicativo sem conflitos de ID e testar os módulos de forma independente.
# 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() — A função de espaço de nomes
Toda função de interface de módulo começa com ns <- NS(id). Todos os IDs de elementos da interface passados ao Shiny devem ser envolvidos em ns(). Isso acrescenta id- a cada ID, criando um espaço de nomes que evita conflitos entre instâncias do 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() — A função do servidor
moduleServer(id, function(input, output, session) {...}) é a forma moderna (Shiny 1.5 ou posterior) de definir a lógica do servidor de um módulo. Dentro da função, input, output e session recebem automaticamente um espaço de nomes — você acessa input$dataset, e não 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])
})
})
}Usando módulos no aplicativo
Chame a função de interface do módulo em ui e a função do servidor do módulo em server, usando a mesma cadeia de caracteres id em ambas. Você pode chamar o mesmo módulo várias vezes com IDs diferentes para criar instâncias independentes.
# 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)Passando parâmetros para a interface do módulo
As funções de interface de módulo são apenas funções R comuns. Adicione parâmetros extras além de id para personalizar a aparência ou o comportamento de cada instância no momento da criação. Esses parâmetros são avaliados uma vez, quando a interface é construída.
# 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')Passando reativos PARA um módulo
As funções do servidor de um módulo podem aceitar valores ou expressões reativas como parâmetros. Isso permite que o aplicativo pai passe dados para os módulos filhos. Dentro do módulo, chame o reativo como uma função para obter seu valor atual.
# 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)
}Retornando reativos DE um módulo
As funções do servidor de um módulo podem retornar valores reativos para o pai. Isso permite que os módulos filhos se comuniquem com o nível superior. Retorne um reativo ou uma lista de reativos de moduleServer() e capture esse retorno na função do servidor pai.
# 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()) })
}Organização dos arquivos dos módulos
Para aplicativos grandes, coloque cada módulo em seu próprio arquivo dentro de uma pasta R/. O Shiny carrega automaticamente todos os arquivos de R/ quando o aplicativo é iniciado. Isso mantém cada módulo autocontido e testável de forma independente usando o pacote 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 aninhados
Os módulos podem conter outros módulos. O módulo pai passa seu próprio espaço de nomes de session para as chamadas dos módulos filhos usando o argumento session. Cada nível de aninhamento acrescenta outro prefixo de espaço de nomes: 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-elementTestando módulos com shinytest2
O pacote shinytest2 permite escrever testes automatizados para módulos, envolvendo-os em um aplicativo mínimo. Use AppDriver para controlar o navegador, definir entradas e verificar valores de saída — tudo sem uma sessão real do navegador.
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')))
})Padrões de comunicação entre módulos
Um resumo dos padrões de comunicação entre módulos e o aplicativo pai:
- Pai para módulo: passe um reativo como parâmetro para o servidor do módulo.
- Módulo para pai: retorne um reativo de moduleServer().
- Entre módulos irmãos: o pai mantém o estado compartilhado (reactiveValues) e o passa para cada módulo.
- Estado global: use
reactiveValuesdefinido no pai e passe referências para os níveis inferiores.
# 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
}Verificação rápida
Por que todos os IDs de elementos da interface em uma função de interface de módulo devem ser envolvidos em ns()?
Revisão dos módulos Shiny
Principais aprendizados sobre módulos Shiny para reutilização de código:
- Os módulos evitam conflitos de ID ao colocar todos os IDs em um espaço de nomes com
NS(id). - Interface do módulo: função comum com
ns <- NS(id); envolva todos os IDs emns(). - Servidor do módulo:
moduleServer(id, function(input, output, session) {...}). - Passe reativos PARA os módulos como parâmetros de função; RETORNE reativos para a comunicação com o nível superior.
- Chame o mesmo módulo várias vezes com IDs diferentes para obter instâncias independentes.
- Organize os módulos em arquivos
R/mod_*.R; o Shiny carrega automaticamente a pastaR/. - Teste os módulos com
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)Perguntas Frequentes
A aula “Módulos Shiny para reutilização de código” é grátis?
Sim — o texto completo de “Módulos Shiny para reutilização de código” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de R Academy, atualize para CoddyKit PRO. O curso de R Academy inclui 4 aulas no total.
O que vou aprender em “Módulos Shiny para reutilização de código”?
Encapsule a lógica da interface e do servidor em módulos com espaço de nomes e reutilizáveis. Você pratica R Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar R Academy?
Nenhuma experiência prévia é necessária. R Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Módulos Shiny para reutilização de código”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de R Academy?
Sim. Cada aula de R Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Aprofundamento em programação reativa
- Módulos Shiny para reutilização de código
- Interface dinâmica com renderUI e insertUI
- Implantando aplicativos Shiny