R : UI / html-tags에서 코드 로직을 우아하게 분리하는 방법은 무엇입니까?


9

문제

ui-elements ( 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가 아닌 파일 및 텍스트 문자열에서만 작동합니다 . 또한 whisker 와 같은 일부 r 패키지를 살펴 보았지만 동일한 제한이있는 것으로 보이며 태그 또는 목록 구조를 지원하지 않습니다.

감사합니다!


CSS 파일을 www폴더 아래에 저장 한 다음 스타일 시트를 적용 할 수 있습니까?
MKa

CSS를 적용하는 경우에는 물론 HTML 구조 등을 변경할 수있는 일반적인 접근 방식을 찾고있었습니다.
Comfort Eagle

커뮤니 케이 션에 의견을 제시하고 의견을 제시하는 데 유용한 것은 없습니다. 이상적으로, htmlTemplate()조건문을 허용하고 알라 핸들 루프 것, 수염은 ... 나뭇 가지

답변:


2

반짝이는 HTML 태그 (또는 htmltools태그) 를 생성하는 함수를 사용하여 구성 가능하고 재사용 가능한 UI 요소를 만드는 것이 좋습니다. 예제 앱에서 "page"요소를 식별 한 다음 두 개의 일반 컨텐츠 컨테이너를 식별 한 후 해당 기능을 작성할 수 있습니다.

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 태그를 뱉어내는 함수입니다. 템플릿 엔진도 작동 할 수 있지만 이미 htmltoolsR의 모든 힘을 얻었을 때 실제 필요는 없습니다 .


응답 해주셔서 감사합니다! 나는 이것처럼 이것을 사용했지만 많은 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.