R: วิธีแยกลอจิกโค้ดอย่างหรูหราจาก UI / html-tags อย่างไร


9

ปัญหา

เมื่อสร้างองค์ประกอบ UI แบบไดนามิก ( shiny.tag,, shiny.tag.list... ) ฉันมักจะพบว่ามันยากที่จะแยกมันออกจากตรรกะรหัสของฉันและมักจะจบลงด้วยระเบียบที่สับสนของซ้อนกันtags$div(...)ผสมกับลูปและงบเงื่อนไข แม้จะดูน่ารำคาญและน่าเกลียด แต่ก็มีข้อผิดพลาดเช่นกันเช่นเมื่อทำการเปลี่ยนแปลงกับแม่แบบ html

ตัวอย่างที่ทำซ้ำได้

สมมติว่าฉันมีโครงสร้างข้อมูลต่อไปนี้:

my_data <- list(
  container_a = list(
    color = "orange",
    height = 100,
    content = list(
      vec_a = c(type = "p", value = "impeach"),
      vec_b = c(type = "h1", value = "orange")
    )
  ),
  container_b = list(
    color = "yellow",
    height = 50,
    content = list(
      vec_a = c(type = "p", value = "tool")
    )
  )  
)

หากตอนนี้ฉันต้องการที่จะผลักดันโครงสร้างนี้เป็น ui-tags ฉันมักจะจบลงด้วยสิ่งที่ชอบ:

library(shiny)

my_ui <- tagList(
  tags$div(
    style = "height: 400px; background-color: lightblue;",
    lapply(my_data, function(x){
      tags$div(
        style = paste0("height: ", x$height, "px; background-color: ", x$color, ";"),
        lapply(x$content, function(y){
          if (y[["type"]] == "h1") {
            tags$h1(y[["value"]])
          } else if (y[["type"]] == "p") {
            tags$p(y[["value"]])
          }
        }) 
      )
    })
  )
)

server <- function(input, output) {}
shinyApp(my_ui, server)

อย่างที่คุณเห็นนี่มันค่อนข้างยุ่งและยังคงไม่มีอะไรเทียบกับตัวอย่างที่แท้จริงของฉัน

ทางออกที่ต้องการ

ฉันหวังว่าจะพบบางสิ่งที่ใกล้กับเครื่องมือสร้างแรงบิดสำหรับ R ซึ่งจะอนุญาตให้กำหนดแม่แบบและข้อมูลแยกจากกัน:

# syntax, borrowed from handlebars.js
my_template <- tagList(
  tags$div(
    style = "height: 400px; background-color: lightblue;",
    "{{#each my_data}}",
    tags$div(
      style = "height: {{this.height}}px; background-color: {{this.color}};",
      "{{#each this.content}}",
      "{{#if this.content.type.h1}}",
      tags$h1("this.content.type.h1.value"),
      "{{else}}",
      tags$p(("this.content.type.p.value")),
      "{{/if}}",      
      "{{/each}}"
    ),
    "{{/each}}"
  )
)

ความพยายามครั้งก่อน

ก่อนอื่นฉันคิดว่าshiny::htmlTemplate()สามารถให้ทางออกได้ แต่วิธีนี้ใช้ได้กับไฟล์และสตริงข้อความเท่านั้นไม่ใช่shiny.tags ฉันยังดูที่ r-packages เช่นwhisker แต่ดูเหมือนว่าจะมีข้อ จำกัด เหมือนกันและไม่สนับสนุนแท็กหรือโครงสร้างรายการ

ขอบคุณ!


คุณสามารถบันทึกไฟล์ css ใต้wwwโฟลเดอร์แล้วใช้สไตล์ชีทได้หรือไม่
MKa

ในกรณีของการใช้ css แน่นอน แต่ฉันกำลังมองหาวิธีการทั่วไปที่ช่วยให้การเปลี่ยนแปลงในโครงสร้าง html ฯลฯ
Comfort Eagle

ไม่มีประโยชน์ที่จะเพิ่ม แต่ upvoting และแสดงความคิดเห็นในการมอบหมาย เป็นการดีที่htmlTemplate()จะอนุญาตให้มีเงื่อนไขและลูป Ala แฮนด์บาร์, หนวด, กิ่งไม้ ...
จะ

คำตอบ:


2

ฉันชอบการสร้างองค์ประกอบ UI ที่สามารถคอมโพสิตและใช้ซ้ำได้โดยใช้ฟังก์ชั่นที่สร้างแท็ก HTML แบบเงา (หรือhtmltoolsแท็ก) จากแอปตัวอย่างของคุณฉันสามารถระบุองค์ประกอบ "หน้า" จากนั้นบรรจุเนื้อหาทั่วไปสองรายการจากนั้นสร้างฟังก์ชันบางอย่างสำหรับสิ่งเหล่านั้น:

library(shiny)

my_page <- function(...) {
  div(style = "height: 400px; background-color: lightblue;", ...)
}

my_content <- function(..., height = NULL, color = NULL) {
  style <- paste(c(
    sprintf("height: %spx", height),
    sprintf("background-color: %s", color)
  ), collapse = "; ")

  div(style = style, ...)
}

จากนั้นฉันก็สามารถเขียน UI ของฉันด้วยสิ่งนี้:

my_ui <- my_page(
  my_content(
    p("impeach"),
    h1("orange"),
    color = "orange",
    height = 100
  ),
  my_content(
    p("tool"),
    color = "yellow",
    height = 50
  )
)

server <- function(input, output) {}
shinyApp(my_ui, server)

เมื่อใดก็ตามที่ฉันต้องการปรับแต่งสไตล์หรือ HTML ขององค์ประกอบฉันก็จะตรงไปที่ฟังก์ชั่นที่สร้างองค์ประกอบนั้น

นอกจากนี้ฉันเพิ่งขีดเส้นใต้ข้อมูลในกรณีนี้ ฉันคิดว่าโครงสร้างข้อมูลในตัวอย่างของคุณผสมผสานข้อมูลกับความกังวลของ UI (การกำหนดสไตล์, แท็ก HTML) ซึ่งอาจอธิบายถึงลักษณะที่ซับซ้อนบางอย่าง ข้อมูลเดียวที่ฉันเห็นคือ "ส้ม" เป็นส่วนหัวและ "impeach" / "เครื่องมือ" เป็นเนื้อหา

หากคุณมีข้อมูลที่ซับซ้อนมากขึ้นหรือต้องการส่วนประกอบ UI เฉพาะเจาะจงมากขึ้นคุณสามารถใช้ฟังก์ชั่นอีกครั้งเช่นแบบเอกสารสำเร็จรูป:

my_content_card <- function(title = "", content = "") {
  my_content(
    h1(title),
    p(content),
    color = "orange",
    height = 100
  )
}

my_ui <- my_page(
  my_content_card(title = "impeach", content = "orange"),
  my_content(
    p("tool"),
    color = "yellow",
    height = 50
  )
)

หวังว่าจะช่วย หากคุณกำลังมองหาตัวอย่างที่ดีกว่านี้คุณสามารถตรวจสอบซอร์สโค้ดที่อยู่ด้านหลังองค์ประกอบอินพุทและเอาท์พุทของ Shiny (เช่นselectInput()) ซึ่งเป็นฟังก์ชั่นหลักที่แยกแท็ก HTML ออก เอ็นจิ้น templating สามารถทำงานได้ แต่ไม่จำเป็นต้องใช้จริงเมื่อคุณได้รับhtmltools+ เต็มกำลังของ R แล้ว


ขอบคุณสำหรับคำตอบ! ฉันเคยทำแบบนี้เช่นกัน แต่มันก็เป็นไปไม่ได้เลยเมื่อ html ส่วนใหญ่ไม่สามารถนำกลับมาใช้ใหม่ได้ ฉันเดาว่าเทมเพลต
Comfort Eagle

1

บางทีคุณอาจจะพิจารณามองและglue()get()

ได้รับ ():

get() สามารถเปลี่ยนสตริงเป็นตัวแปร / วัตถุ

ดังนั้นคุณสามารถย่อ:

if (y[["type"]] == "h1") {
    tags$h1(y[["value"]])
} else if (y[["type"]] == "p") {
    tags$p(y[["value"]])
}

ถึง

get(y$type)(y$value)

(ดูตัวอย่างด้านล่าง)

กาว():

glue()paste0()ให้ทางเลือกกับผู้ มันสามารถอ่านได้มากขึ้นถ้าคุณเน้นที่สตริงและตัวแปรจำนวนมากกับสตริง ฉันถือว่ามันยังอยู่ใกล้กับไวยากรณ์ของผลลัพธ์ที่คุณต้องการ

แทน:

paste0("height: ", x$height, "px; background-color: ", x$color, ";")

คุณจะเขียน:

glue("height:{x$height}px; background-color:{x$color};")

ตัวอย่างของคุณจะง่ายต่อการ:

tagList(
  tags$div(style = "height: 400px; background-color: lightblue;",
    lapply(my_data, function(x){
      tags$div(style = glue("height:{x$height}px; background-color:{x$color};"),
        lapply(x$content, function(y){get(y$type)(y$value)}) 
      )
    })
  )
)

โดยใช้:

library(glue)
my_data <- list(
  container_a = list(
    color = "orange",
    height = 100,
    content = list(
      vec_a = list(type = "p", value = "impeach"),
      vec_b = list(type = "h1", value = "orange")
    )
  ),
  container_b = list(
    color = "yellow",
    height = 50,
    content = list(
      vec_a = list(type = "p", value = "tool")
    )
  )  
)

ทางเลือก:

ผมคิดว่า htmltemplate เป็นความคิดที่ดี แต่ปัญหาอื่นที่มีช่องว่างที่ไม่พึงประสงค์: https://github.com/rstudio/htmltools/issues/19#issuecomment-252957684


ขอบคุณสำหรับข้อมูลของคุณ ในขณะที่รหัสของคุณมีขนาดเล็กลงปัญหาของการผสม html และตรรกะยังคงอยู่ : /
Comfort Eagle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.