R: Kod mantığını UI / html etiketlerinden zarif bir şekilde nasıl ayırırım?


9

Sorun

Dinamik olarak ui-elements ( shiny.tag,, shiny.tag.list...) oluştururken, genellikle kod mantığımdan ayırmakta zorlanır ve genellikle iç içe geçmiş tags$div(...), döngüler ve koşullu ifadelerle karıştırılmış kıvrımlı bir karmaşa ile sonuçlanır . Sinir bozucu ve çirkin görünmekle birlikte, örneğin html şablonlarında değişiklik yaparken de hataya açıktır.

Tekrarlanabilir örnek

Diyelim ki aşağıdaki veri yapısına sahibim:

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

Şimdi bu yapıyı ui-etiketlerine itmek istiyorsam, genellikle şöyle bir şeyle sonuçlanırım:

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)

Gördüğünüz gibi, bu zaten oldukça dağınık ve gerçek benzeri örneklerime kıyasla hala hiçbir şey yok.

İstenen çözüm

Şablonları ve verileri ayrı ayrı tanımlayabilecek R için cazip bir motora yakın bir şey bulmayı umuyordum :

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

Önceki denemeler

İlk olarak, bunun shiny::htmlTemplate()bir çözüm sunabileceğini düşündüm , ancak bu sadece dosyalar ve metin dizeleriyle çalışır, shiny.tags değil . Ayrıca bıyık gibi bazı r-paketlerine de baktım , ancak bunlar aynı sınırlamaya sahip görünüyor ve etiketleri veya liste yapılarını desteklemiyor.

Teşekkür ederim!


Bir css dosyasını wwwklasör altına kaydedip stil sayfalarını uygulayabilir misiniz?
MKa

Css uygulanması durumunda, elbette, ama html yapısında vb. Değişikliklere izin veren genel bir yaklaşım arıyordum.
Comfort Eagle

Eklemek için yararlı bir şey yok, komisyonda yorum ve yorum yapmak. İdeal olarak, htmlTemplate()koşullu ve döngüler ala gidon, bıyık, dal izin verir
Will

Yanıtlar:


2

Parlak HTML etiketleri (veya htmltoolsetiketleri) üreten işlevleri kullanarak oluşturulabilir ve yeniden kullanılabilir UI öğeleri oluşturmayı seviyorum . Örnek uygulamanızdan, bir "sayfa" öğesini ve ardından iki genel içerik kapsayıcısını tanımlayabilir ve bunlar için bazı işlevler oluşturabilirim:

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

Ve sonra kullanıcı arayüzümü şöyle bir şeyle oluşturabilirim:

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)

Bir öğenin stilini veya HTML'sini değiştirmem gerektiğinde, doğrudan o öğeyi üreten işleve giderdim.

Ayrıca, bu durumda verileri daha önce işaretledim. Örneğinizdeki veri yapısının gerçekten kıvrımlılıktan bazılarını açıklayabilecek UI endişeleriyle (stil, HTML etiketleri) verileri karıştırdığını düşünüyorum. Gördüğüm tek veri başlık olarak "turuncu" ve içerik olarak "örnek" / "araç".

Daha karmaşık verileriniz varsa veya daha spesifik UI bileşenlerine ihtiyacınız varsa, yapı taşları gibi işlevleri tekrar kullanabilirsiniz:

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

Umarım yardımcı olur. Daha iyi örnekler arıyorsanız selectInput(), esasen HTML etiketlerini tüküren işlevler olan Shiny'nin giriş ve çıkış öğelerinin (örn. ) Arkasındaki kaynak kodunu kontrol edebilirsiniz . Geçici bir motor da işe yarayabilir, ancak zaten htmltoolsR'nin + gücüne sahip olduğunuzda gerçek bir ihtiyaç yoktur .


Cevap için teşekkür ederim! Ben de bunu böyle yapardım ama html'nin çoğu tekrar kullanılamadığında oldukça pratik olmaz. Sanırım bir çeşit şablon motoru tek geçerli çözüm olurdu: /
Comfort Eagle

1

Belki glue()ve 'ye bakmayı düşünebilirsiniz get().

almak():

get() dizeleri değişkenlere / nesnelere dönüştürebilir.

Böylece kısaltabilirsiniz:

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

için

get(y$type)(y$value)

(aşağıdaki örneğe bakın).

tutkal():

glue()bir alternatif sunar paste0(). Çok sayıda dizeyi ve değişkeni bir dizeye yoğunlaştırırsanız daha okunabilir olabilir. İstediğiniz sonucun sözdizimine de yakın göründüğünü düşünüyorum.

Onun yerine:

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

Sen şöyle yazardın:

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

Örneğin aşağıdakileri basitleştirir:

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

Kullanımı:

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

Alternatifler:

Htmltemplate iyi bir fikir olduğunu düşünüyorum, ancak başka bir sorun istenmeyen boşluklar: https://github.com/rstudio/htmltools/issues/19#issuecomment-252957684 .


Girdiniz için teşekkürler. Kodunuz daha kompakt olsa da, html ve mantık karıştırma sorunu devam eder. : /
Comfort Eagle
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.