R Academy · レッスン

renderUI と insertUI による動的 UI

ユーザー入力とサーバーの状態に応じて、UI 要素をその場で生成します。

レッスン 3/413 ステップ

「renderUI と insertUI による動的 UI」はCoddyKit上の無料R Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

動的UI — 理由と使いどき

アプリの起動時に必要なUIを完全には決定できない場合があります。ユーザーの選択、読み込んだデータ、実行時の条件に依存するためです。Shinyには2つの仕組みがあります。renderUI()はプレースホルダーの内容全体を置き換え、insertUI()/removeUI()はすべてを置き換えずに要素を追加または削除します。

library(shiny)

# Static UI: columns always shown
# ui <- fluidPage(selectInput('x', ...), selectInput('y', ...))

# Dynamic UI: columns depend on which dataset was loaded
# ui <- fluidPage(fileInput('upload', ...), uiOutput('var_selectors'))

renderUI() と uiOutput()

サーバー内のrenderUI({...})は、リアクティブにUI要素を構築して返します。UI内のuiOutput('id')は、renderUIが生成した内容を表示するプレースホルダーを作成します。リアクティブな処理が再実行されるたびに、プレースホルダーの内容全体が置き換えられます。

ui <- fluidPage(
  selectInput('dataset', 'Dataset:', choices = c('iris', 'mtcars')),
  uiOutput('column_selector')   # placeholder
)

server <- function(input, output, session) {
  output$column_selector <- renderUI({
    df <- get(input$dataset)  # reactive dependency on dataset
    selectInput('col', 'Choose Column:',
                choices = names(df))
  })
}

shinyApp(ui, server)

複数のコントロールで renderUI() を使う

renderUI()からtagList()を返すと、複数のUI要素を一度に描画できます。データに応じてコントロールの数が変わる場合の標準的なパターンです。たとえば、データセットの各列に1つずつフィルターを生成する場合に使います。

server <- function(input, output, session) {
  output$filters <- renderUI({
    df    <- get(input$dataset)
    nums  <- names(df)[sapply(df, is.numeric)]

    # One slider per numeric column
    sliders <- lapply(nums, function(col) {
      rng <- range(df[[col]], na.rm = TRUE)
      sliderInput(
        inputId = paste0('filter_', col),
        label   = col,
        min     = rng[1], max = rng[2],
        value   = rng
      )
    })
    tagList(sliders)   # render all sliders
  })
}

insertUI() — 要素を動的に追加する

insertUI(selector, where, ui)は、ページ全体を再構築せずにDOMへUI要素を挿入します。挿入位置の指定にはCSSセレクターを使用します。whereには'afterEnd'、'beforeEnd'、'afterBegin'、'beforeBegin'を指定できます。

server <- function(input, output, session) {
  item_count <- reactiveVal(0)

  observeEvent(input$add_item, {
    item_count(item_count() + 1)
    id <- paste0('item_', item_count())

    insertUI(
      selector = '#item_container',  # CSS selector
      where    = 'beforeEnd',
      ui       = div(
        id    = id,
        style = 'padding: 5px; border: 1px solid #ccc; margin: 3px;',
        textInput(id, paste('Item', item_count()), value = '')
      )
    )
  })
}

removeUI() — 要素を削除する

removeUI(selector)は、CSSセレクターに一致する最初のDOM要素を削除します。すべての一致要素を削除するにはmultiple = TRUEを使用します。ユーザーが項目を追加・削除できるリスト型UIで、insertUI()と対になる機能です。

server <- function(input, output, session) {
  observeEvent(input$remove_last, {
    # Remove the last item div
    n <- isolate(item_count())
    if (n > 0) {
      removeUI(selector = paste0('#item_', n))
      item_count(n - 1)
    }
  })

  observeEvent(input$clear_all, {
    # Remove all items matching a class
    removeUI(selector = '.dynamic-item', multiple = TRUE)
    item_count(0)
  })
}

conditionalPanel() — クライアント側の表示制御

conditionalPanel(condition, ...)は、ブラウザーで評価されるJavaScript条件に基づいてUI要素を表示または非表示にします。renderUI()とは異なり、要素は常にDOM内に存在し、表示または非表示になるだけです。サーバーとの往復通信が不要なため、単純な表示・非表示の制御ではこちらの方が高速です。

ui <- fluidPage(
  selectInput('plot_type', 'Plot Type:',
              choices = c('scatter', 'histogram', 'boxplot')),

  # Only shown when scatter is selected
  conditionalPanel(
    condition = "input.plot_type == 'scatter'",
    selectInput('x_var', 'X Variable:', choices = names(mtcars)),
    selectInput('y_var', 'Y Variable:', choices = names(mtcars))
  ),

  # Only shown when histogram is selected
  conditionalPanel(
    condition = "input.plot_type == 'histogram'",
    sliderInput('bins', 'Number of bins:', 5, 50, 20)
  ),

  plotOutput('main_plot')
)

updateSelectInput() — 既存の入力を変更する

入力コントロール全体を置き換える代わりに、updateSelectInput(session, id, choices, selected)を使うと、既存のselectInputの選択肢または選択値だけを更新できます。すべての入力タイプに同様の更新関数があり、updateSliderInput()、updateTextInput()などがあります。

server <- function(input, output, session) {
  # When dataset changes, update the column selector
  observeEvent(input$dataset, {
    df <- get(input$dataset)
    updateSelectInput(
      session  = session,
      inputId  = 'column',
      choices  = names(df),
      selected = names(df)[1]
    )
  })

  # Reset slider when reset button pressed
  observeEvent(input$reset_btn, {
    updateSliderInput(session, 'n_items',
                     value = 20, min = 1, max = 100)
  })
}

updateSliderInput() と updateNumericInput()

更新関数はブラウザーにメッセージを送り、既存の入力の現在値、最小値、最大値、ラベルを変更します。renderUI()でコントロールを破棄して再作成するより効率的で、ページレイアウト内の位置も維持されます。

server <- function(input, output, session) {
  # When data loads, set slider max to actual data length
  observeEvent(input$load_data, {
    data <- readRDS(input$upload$datapath)

    updateSliderInput(session, 'row_slider',
      min   = 1,
      max   = nrow(data),
      value = c(1, min(50, nrow(data)))
    )

    updateNumericInput(session, 'page_size',
      max   = nrow(data),
      value = min(10, nrow(data))
    )
  })
}

req() で renderUI の入力にアクセスする

renderUI()で動的に作成された入力は、描画されるまでNULLです。動的に作成された入力に依存するサーバーコードでは、初期化中のエラーを防ぐためにreq(input$dynamic_input)を使用します。

server <- function(input, output, session) {
  # dynamic input: 'col' is created by renderUI
  output$column_selector <- renderUI({
    selectInput('col', 'Column:', choices = names(get(input$dataset)))
  })

  output$summary_stats <- renderPrint({
    req(input$col)           # wait until 'col' exists
    req(input$dataset)       # and dataset is chosen
    df <- get(input$dataset)
    summary(df[[input$col]])
  })
}

appendTab() による動的なタブ

appendTab(inputId, tab, select)は、実行時にtabsetPanel()へ新しいタブを追加します。removeTab(inputId, target)はタブを1つ削除します。このパターンにより、ユーザーが動的に生成された分析ビューを開いたり、閉じたり、切り替えたりできるワークフローを実現できます。

ui <- fluidPage(
  actionButton('add_tab', 'Open New Analysis Tab'),
  tabsetPanel(id = 'analysis_tabs', type = 'tabs')
)

server <- function(input, output, session) {
  tab_count <- reactiveVal(0)

  observeEvent(input$add_tab, {
    tab_count(tab_count() + 1)
    n <- tab_count()
    appendTab(
      inputId = 'analysis_tabs',
      select  = TRUE,
      tab = tabPanel(
        title = paste('Analysis', n),
        value = paste0('tab_', n),
        plotOutput(paste0('plot_', n)),
        actionButton(paste0('close_', n), 'Close')
      )
    )
  })
}

renderUI と insertUI — どちらを使うか

用途に応じて適切なツールを選択してください:

  • renderUI(): プレースホルダーの内容全体をリアクティブに置き換えます。ブロック全体が一緒に変化する場合に最適です。
  • insertUI(): 既存の内容に新しい要素を追加します。すべてを再描画せずにリストを増やしたい場合に最適です。
  • updateXxxInput(): 既存の入力をその場で変更します。再描画せずに選択肢や値を変更する場合に最適です。
  • conditionalPanel(): サーバーとの往復通信なしで表示・非表示を切り替えます。単純な表示制御に最適です。
# Decision guide:
# - 'Show filter only when advanced mode' -> conditionalPanel
# - 'Populate column choices from loaded CSV' -> updateSelectInput
# - 'Show different controls per selected tab' -> renderUI
# - 'Add a row editor for each row user adds' -> insertUI
# - 'Remove a row editor' -> removeUI

# Combining strategies:
server <- function(input, output, session) {
  # Use renderUI for column selectors (replaces whole block)
  output$col_selectors <- renderUI({ ... })
  # Use insertUI for dynamically added rows
  observeEvent(input$add_row, { insertUI(...) })
  # Use conditionalPanel for show/hide (in UI, no server code)
}

確認問題

Shinyで動的UIを構築する場合、renderUI()とinsertUI()の主な違いは何ですか。

動的UIのまとめ

renderUIとinsertUIによる動的UIの要点:

  • renderUI({}) + uiOutput('id'): プレースホルダーの内容全体をリアクティブに置き換えます。
  • renderUI()からtagList()を返すと、複数の要素を一度に描画できます。
  • insertUI(selector, where, ui): DOM内の任意の場所に要素を挿入します。
  • removeUI(selector): CSSセレクターで要素を削除します。
  • conditionalPanel(condition, ...): サーバーを介さずにクライアント側で表示・非表示を切り替えます。
  • updateSelectInput()、updateSliderInput(): 既存の入力をその場で変更します。
  • 動的な入力が描画される前のNULLに備えて、必ずreq(input$dynamic_id)を使用してください。
# Typical pattern: dynamic column selector
ui <- fluidPage(
  fileInput('upload', 'Upload CSV'),
  uiOutput('col_ui'),          # placeholder
  plotOutput('col_plot')
)

server <- function(input, output, session) {
  df <- reactive({
    req(input$upload)
    read.csv(input$upload$datapath)
  })

  output$col_ui <- renderUI({
    req(df())
    selectInput('col', 'Column:', choices = names(df()))
  })

  output$col_plot <- renderPlot({
    req(input$col)
    hist(df()[[input$col]])
  })
}
無料で開始

AI チューターと学ぶ R — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
43
レッスン
159

よくある質問

「renderUI と insertUI による動的 UI」レッスンは無料ですか?

はい。「renderUI と insertUI による動的 UI」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「renderUI と insertUI による動的 UI」で何を学びますか?

ユーザー入力とサーバーの状態に応じて、UI 要素をその場で生成します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「renderUI と insertUI による動的 UI」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. リアクティブプログラミングの深掘り
  2. コード再利用のための Shiny モジュール
  3. renderUI と insertUI による動的 UI
  4. Shiny アプリをデプロイする
← R Academyに戻る