0Pricing
R Academy · บทเรียน

ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI

สร้างองค์ประกอบส่วนติดต่อผู้ใช้ขณะทำงานตามอินพุตของผู้ใช้และสถานะเซิร์ฟเวอร์

ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI เป็นบทเรียน R Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน R Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน

ส่วนติดต่อผู้ใช้แบบไดนามิก — เหตุผลและช่วงเวลาที่ควรใช้

บางครั้งไม่สามารถกำหนดส่วนติดต่อผู้ใช้ที่ต้องการได้ครบถ้วนตั้งแต่เริ่มต้นแอป เพราะขึ้นอยู่กับตัวเลือกของผู้ใช้ ข้อมูลที่โหลด หรือเงื่อนไขขณะทำงาน Shiny มีกลไกสองแบบ: 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({...}) ในเซิร์ฟเวอร์จะสร้างและส่งคืนองค์ประกอบส่วนติดต่อผู้ใช้แบบรีแอกทีฟ ส่วน 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() กับตัวควบคุมหลายรายการ

ส่งคืน tagList() จาก renderUI() เพื่อแสดงองค์ประกอบส่วนติดต่อผู้ใช้หลายรายการพร้อมกัน นี่เป็นรูปแบบมาตรฐานเมื่อจำนวนตัวควบคุมขึ้นอยู่กับข้อมูล เช่น การสร้างตัวกรองหนึ่งรายการต่อหนึ่งคอลัมน์ในชุดข้อมูล

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 โดยไม่ต้องสร้างทั้งหน้าใหม่ ใช้ตัวเลือก 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) ลบองค์ประกอบ DOM แรกที่ตรงกับตัวเลือก CSS ใช้ multiple = TRUE เพื่อลบองค์ประกอบที่ตรงกันทั้งหมด ฟังก์ชันนี้ทำงานคู่กับ 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 ที่ประเมินในเบราว์เซอร์ ต่างจาก 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))
    )
  })
}

การเข้าถึงอินพุตของ renderUI ด้วย req()

อินพุตที่สร้างแบบไดนามิกด้วย 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) ใช้ลบแท็บหนึ่งรายการ รูปแบบนี้ช่วยให้ผู้ใช้เปิด ปิด และสลับระหว่างมุมมองการวิเคราะห์ที่สร้างแบบไดนามิกได้

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

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ความแตกต่างที่สำคัญระหว่าง renderUI() และ insertUI() ในการสร้างส่วนติดต่อผู้ใช้แบบไดนามิกใน Shiny คืออะไร

ทบทวนส่วนติดต่อผู้ใช้แบบไดนามิก

ประเด็นสำคัญจากบทส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI:

  • renderUI({}) + uiOutput('id'): แทนที่เนื้อหาทั้งหมดของพื้นที่สำรองแบบรีแอกทีฟ
  • ส่งคืน tagList() จาก renderUI() เพื่อแสดงองค์ประกอบหลายรายการพร้อมกัน
  • insertUI(selector, where, ui): แทรกองค์ประกอบลงในตำแหน่งใดก็ได้ของ DOM
  • removeUI(selector): ลบองค์ประกอบตามตัวเลือก CSS
  • conditionalPanel(condition, ...): แสดงหรือซ่อนฝั่งไคลเอ็นต์โดยไม่ต้องใช้เซิร์ฟเวอร์
  • updateSelectInput(), updateSliderInput(): แก้ไขอินพุตที่มีอยู่ในตำแหน่งเดิม
  • ใช้ req(input$dynamic_id) เสมอเพื่อป้องกันค่า NULL ก่อนที่อินพุตแบบไดนามิกจะแสดงผล
# 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]])
  })
}

คำถามที่พบบ่อย

บทเรียน “ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส R Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI”

สร้างองค์ประกอบส่วนติดต่อผู้ใช้ขณะทำงานตามอินพุตของผู้ใช้และสถานะเซิร์ฟเวอร์ คุณปฏิบัติ R Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน R Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน R Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน R Academy นี้ได้ไหม

ได้ บทเรียน R Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เจาะลึกการเขียนโปรแกรมแบบรีแอกทีฟ
  2. โมดูล Shiny เพื่อการนำโค้ดกลับมาใช้ใหม่
  3. ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI
  4. การนำแอป Shiny ไปใช้งาน
← กลับไปที่ R Academy