0Pricing
R Academy · Lekcja

Moduły Shiny wielokrotnego użytku

Hermetyzuj logikę interfejsu użytkownika i serwera w modułach z przestrzenią nazw, które można ponownie wykorzystywać.

Moduły Shiny wielokrotnego użytku to bezpłatna lekcja R Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej R Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs R Academy zawiera 4 lekcji w sumie.

Dlaczego warto używać modułów Shiny?

W miarę rozrastania się aplikacji Shiny umieszczanie całego kodu interfejsu użytkownika i serwera w jednym pliku staje się trudne do zarządzania. Moduły to samodzielne elementy interfejsu użytkownika Shiny i logiki serwera z identyfikatorami z przestrzenią nazw. Można wielokrotnie używać tego samego modułu w jednej aplikacji bez konfliktów identyfikatorów, a także niezależnie testować moduły.

# 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-dataset

NS() — funkcja przestrzeni nazw

Każda funkcja interfejsu użytkownika modułu rozpoczyna się od ns <- NS(id). Wszystkie identyfikatory elementów interfejsu użytkownika przekazywane do Shiny muszą być opakowane w ns(). Funkcja ta dodaje prefiks id- do każdego identyfikatora, tworząc przestrzeń nazw, która zapobiega konfliktom między instancjami modułu.

# 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() — funkcja serwera

moduleServer(id, function(input, output, session) {...}) to nowoczesny (dostępny od Shiny 1.5) sposób definiowania logiki serwera modułu. Wewnątrz funkcji elementy input, output i session automatycznie korzystają z przestrzeni nazw — uzyskuje się dostęp do input$dataset, a nie do 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])
    })
  })
}

Używanie modułów w aplikacji

Wywołaj funkcję interfejsu użytkownika modułu w ui, a funkcję serwera modułu w server, używając w obu przypadkach tego samego ciągu id. Możesz wywołać ten sam moduł wielokrotnie z różnymi identyfikatorami, aby utworzyć niezależne instancje.

# 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)

Przekazywanie parametrów do interfejsu modułu

Funkcje interfejsu użytkownika modułu są zwykłymi funkcjami R. Dodaj parametry oprócz id, aby dostosować wygląd lub działanie każdej instancji w momencie jej tworzenia. Parametry te są obliczane jednokrotnie podczas budowania interfejsu użytkownika.

# 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')

Przekazywanie wartości reaktywnych DO modułu

Funkcje serwera modułu mogą przyjmować wartości reaktywne lub wyrażenia reaktywne jako parametry. Dzięki temu aplikacja nadrzędna może przekazywać dane do modułów podrzędnych. W module należy wywołać funkcję reaktywną jak funkcję, aby uzyskać jej bieżącą wartość.

# 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)
}

Zwracanie wartości reaktywnych Z modułu

Funkcje serwera modułu mogą zwracać wartości reaktywne do elementu nadrzędnego. Umożliwia to komunikację modułów podrzędnych w górę hierarchii. Zwróć wartość reaktywną lub listę wartości reaktywnych z moduleServer() i przypisz ją w funkcji serwera nadrzędnego.

# 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()) })
}

Organizacja plików modułów

W dużych aplikacjach umieść każdy moduł w osobnym pliku w folderze R/. Shiny automatycznie wczytuje wszystkie pliki z folderu R/ podczas ładowania aplikacji. Dzięki temu każdy moduł pozostaje samodzielny i można go niezależnie testować za pomocą pakietu 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)

Moduły zagnieżdżone

Moduły mogą zawierać inne moduły. Moduł nadrzędny przekazuje własną przestrzeń nazw session do wywołań modułów podrzędnych za pomocą argumentu session. Każdy poziom zagnieżdżenia dodaje kolejny prefiks przestrzeni nazw: 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-element

Testowanie modułów za pomocą shinytest2

Pakiet shinytest2 umożliwia pisanie automatycznych testów modułów przez opakowanie ich w minimalną aplikację. Użyj AppDriver, aby sterować przeglądarką, ustawiać dane wejściowe i sprawdzać wartości wyjściowe — bez korzystania z rzeczywistej sesji przeglądarki.

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')))
})

Wzorce komunikacji między modułami

Podsumowanie wzorców komunikacji między modułami a aplikacją nadrzędną:

  • Element nadrzędny do modułu: przekaż wartość reaktywną jako parametr do serwera modułu.
  • Moduł do elementu nadrzędnego: zwróć wartość reaktywną z moduleServer().
  • Między modułami równorzędnymi: element nadrzędny przechowuje współdzielony stan (reactiveValues) i przekazuje go do każdego modułu.
  • Stan globalny: użyj reactiveValues zdefiniowanego w elemencie nadrzędnym i przekaż do modułów odwołania do niego.
# 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
}

Szybkie sprawdzenie

Dlaczego wszystkie identyfikatory elementów interfejsu użytkownika w funkcji interfejsu modułu muszą być opakowane w ns()?

Podsumowanie modułów Shiny

Najważniejsze informacje z lekcji „Moduły Shiny na potrzeby ponownego użycia kodu”:

  • Moduły zapobiegają konfliktom identyfikatorów, dodając do wszystkich identyfikatorów przestrzeń nazw za pomocą NS(id).
  • Interfejs modułu: zwykła funkcja z ns <- NS(id); wszystkie identyfikatory należy opakować w ns().
  • Serwer modułu: moduleServer(id, function(input, output, session) {...}).
  • Przekazuj wartości reaktywne DO modułów jako parametry funkcji; ZWRACAJ wartości reaktywne, aby umożliwić komunikację w górę hierarchii.
  • Wywołuj ten sam moduł wielokrotnie z różnymi identyfikatorami, aby tworzyć niezależne instancje.
  • Organizuj moduły w plikach R/mod_*.R; Shiny automatycznie wczytuje folder R/.
  • Testuj moduły za pomocą 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)

Często zadawane pytania

Czy lekcja „Moduły Shiny wielokrotnego użytku” jest bezpłatna?

Tak — pełny tekst „Moduły Shiny wielokrotnego użytku” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu R Academy, przejdź na CoddyKit PRO. Kurs R Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Moduły Shiny wielokrotnego użytku”?

Hermetyzuj logikę interfejsu użytkownika i serwera w modułach z przestrzenią nazw, które można ponownie wykorzystywać. Ćwiczysz R Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć R Academy?

Nie wymagamy żadnego doświadczenia. R Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Moduły Shiny wielokrotnego użytku”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji R Academy?

Tak. Każda lekcja R Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Zaawansowane programowanie reaktywne
  2. Moduły Shiny wielokrotnego użytku
  3. Dynamiczny interfejs za pomocą renderUI i insertUI
  4. Wdrażanie aplikacji Shiny
← Powrót do R Academy