8 Módulos Shiny
8.1 Introdução
Agora vamos falar sobre o sistema de módulos do shiny, que permite extrair a função ui acoplada e o código server em componentes isolados e reutilizáveis, além de deixar seu aplicativo mais limpo e organizado.
À medida que os aplicativos shiny ficam maiores e mais complicados, usamos módulos para gerenciar a complexidade crescente do código do aplicativo.
Um módulo é um par de funções ui e server. A mágica dos módulos vem porque essas funções são construídas de uma maneira especial que cria um “namespace.” Até agora, ao escrever um aplicativo, os nomes dos controles id
são globais: todas as partes de sua função server podem ver todas as partes da função ui. Os módulos oferecem a capacidade de criar controles que só podem ser vistos de dentro do módulo. Isso é chamado de namespace porque cria “espaços” de “nomes” que são isolados do resto do aplicativo.
Módulos shiny têm duas grandes vantagens:
O namespacing torna mais fácil entender como seu aplicativo funciona porque você pode escrever, analisar e testar componentes individuais de forma isolada.
Como os módulos são funções, eles ajudam a reutilizar o código; tudo o que você pode fazer com uma função, você pode fazer com um módulo.
8.2 Módulo simples
Vamos criar um aplicativo bem simples que desenha um histograma, que servirá para ilustrar a mecânica básica do funcionamento do módulo.
library(shiny)
<- fluidPage(
ui selectInput('var', 'Variável', names(iris)),
numericInput('bins', 'bins', 10, min = 1),
plotOutput('hist')
)<- function(input, output, session) {
server <- reactive(iris[[input$var]])
data $hist <- renderPlot({
outputhist(data(), breaks = input$bins, main = input$var)
res = 96)
},
}shinyApp(ui = ui, server = server)
Como um aplicativo, o módulo é composto por duas partes:
A função de interface do usuário do módulo que gera a especificação
ui
.A função de servidor do módulo que executa o código dentro da função
server
.
As duas funções possuem formulários padrão. Ambos pegam um argumento id
e o usam para definir o namespace do módulo. Para criar um módulo, precisamos extrair o código da ui e do server do aplicativo e colocá-los na arquivo do módulo.
8.2.1 Módulo UI
No módulo ui existem duas etapas:
Coloca o código dentro de uma função que tenha um argumento
id
.Envolva cada ID exsitente em uma chamada
NS()
.
Obs: a função NS()
cria IDs com namepace a partir de IDs vazios, os aplicativos Shiny usam IDs para identificar entradas e saídas. Esses IDs devem ser exclusivos em um aplicativo, pois o uso acidental do mesmo ID de entrada/saída mais de uma vez resultará em um comportamento inesperado. A solução tradicional para evitar conflitos de nomes são os namespaces; um namespace está para um ID como um diretório está para um arquivo. Use a função NS()
para transformar um ID simples em um espaço de nomes, combinando-os com os intermediários ns.sep
.
<- function(id) {
histUI tagList(
selectInput(NS(id, 'var'), 'Variável', choices = names(iris)),
numericInput(NS(id, 'bins'), 'bins', value = 10, min = 1),
plotOutput(NS(id, 'hist'))
) }
Aqui, os componentes da ui foram retornado em a tagList()
, que é um tipo especial de função de layout que permite agrupar vários componentes sem realmente sugerir como eles serão dispostos. É responsabilidade de quem está ligando histUI()
envolver o resultado em uma função de layout como column()
ou de fluidRow()
acordo com suas necessidades.
8.2.2 Módulo Server
A função server é empacotada dentro de outra função que deve ter um argumento id
. Esta função chama moduleServer()
com o id
e uma função que se parece com uma função de servidor normal:
<- function(id) {
histServer moduleServer(id, function(input, output, session) {
<- reactive(iris[[input$var]])
data $hist <- renderPlot({
outputhist(data(), breaks = input$bins, main = input$var)
res = 96)
},
}) }
Observe que moduleServer()
cuida do namespace automaticamente: dentro demoduleServer(id)
, input$var
e input$bins
consulta as entradas com nomes NS(id, 'var')
e NS(id, 'bins')
.
Aplicativo atualizado
<- function() {
histogramApp <- fluidPage(
ui histUI('hist1')
)<- function(input, output, session) {
server histServer('hist1')
}shinyApp(ui = ui, server = server)
}
histogramApp() # chamando o aplicativo
8.3 Entradas e Saídas
Os módulos podem representar entrada, saída ou ambos. Eles podem ser tão simples quanto uma única saída ou tão complicados quanto uma interface com várias guias repleta de controles/saídas orientados por múltiplas expressões reativas e observers.
Ás vezes, um módulo com apenas um argumento id
para o módulo ui e o server é útil porque permite isolar o código complexo em seu próprio arquivo. Isso é particularmente útil para aplicativos que agregam componentes independentes, como um painel corporativo onde cada guia mostra relatórios personalizados para cada linha de negócios. Aqui, os módulos permitem que você desenvolva cada peça em seu próprio arquivo sem ter que se preocupar com o conflito de IDs entre os componentes.
Muitas vezes, no entanto, o módulo ui e o server precisarão de argumentos adicionais. Adicionar argumentos à ui do módulo oferece maior controle sobre a aparência do módulo, permitindo que você use o mesmo em mais lugares em seu aplicativo. Mas a ui é apenas uma função R regular, portanto, há relativamente pouco a aprender que seja específico do shiny. Ao contrário do código shiny regular, conectar módulos requer que você seja explícito sobre entradas e saídas. Inicialmente, isso vai parecer cansativo. E é certamente mais trabalhoso do que a associação de forma livre usual de shiny. Mas os módulos impõem linhas específicas de comunicação por um motivo: eles são um pouco mais trabalhosos de criar, mas muito mais fáceis de entender e permitem que você crie aplicativos substancialmente mais complexos.
Para ver como as entradas e saídas funcionam, vamos fazer um aplicativo simples, mas que ilustra alguns dos princípios básicos. Vamos criar um módulo que permite ao usuário selecionar um conjunto de dados a partir dos dados integrados fornecidos pelo pacote de conjuntos de dados.
Obs: a função de um módulo ui deve ser um nome que é o sufixo Input
, Output
ou UI
; por exemplo, datasetInput
, selecNumOutput
, ou analiseUI
.
Módulo UI:
<- function(id, filter = NULL) {
datasetInput <- ls('package:datasets')
names if (!is.null(filter)) {
<- lapply(names, get, 'package:datasets')
data <- names[vapply(data, filter, logical(1))]
names
}selectInput(NS(id, 'dataset'), 'Escolha um conjunto de dados:', choices = names)
}
Aqui, usamos um argumento adicional que possa limitar as opções do conjuntos de dados integrados que são data.frame (filter = is.data.frame) ou matrizes (filter = is.matrix). Este argumento filtra opcionalmente os objetos encontrados no pacote de conjuntos de dados e, em seguida, criamos um selectInput
.
Módulo Server:
Usamos o get
para recuperar o conjunto de dados com seu nome.
<- function(id) {
datasetServer moduleServer(id, function(input, output, session) {
reactive(get(input$dataset, 'package:datasets'))
}) }
Criando o aplicativo com os módulos datasetInput
e datasetServer
:
<- function(filter = NULL) {
datasetApp <- fluidPage(
ui datasetInput('dataset', filter = filter),
tableOutput('data')
)<- function(input, output, session) {
server <- datasetServer('dataset')
data $data <- renderTable(head(data()))
output
}shinyApp(ui = ui, server = server)
}
datasetApp()
Para usar o valor retornado no módulo datasetServer
, só precisamos capturar o seu valor de retorno com (data <- datasetServer('dataset')
) e exibimos o conjunto de dados no tableOutput()
. É necessário um argumento filter
que é passado para o módulo ui, facilitando a experiência com esse argumento de entrada.