R:コードロジックをUI / htmlタグからエレガントに分離する方法は?


9

問題

動的なUI要素を作成する場合(shiny.tagshiny.tag.list、...)、私はしばしばそれが困難な私のコードのロジックから分離し、通常、入れ子になったの複雑な混乱で終わるを見つけるtags$div(...)ループや条件文と混合し、。見るのが面倒で見苦しい一方で、たとえばhtml-templatesに変更を加える場合など、エラーが発生しやすくなります。

再現可能な例

次のデータ構造があるとします。

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タグにプッシュしたい場合、通常は次のようになります。

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パッケージも調べましたが、それらには同じ制限があり、タグやリスト構造をサポートしていないようです。

ありがとうございました!


あなたはwwwフォルダの下にCSSファイルを保存してからスタイルシートを適用できますか?
MKa

必ず、CSSを適用する場合には、私は、HTML等の構造の変化を可能にする一般的な方法を探していた
コンフォートイーグル

追加するのに役立つものは何もありませんが、お悔やみに賛成投票とコメントをします。理想的には、htmlTemplate()条件分岐やループALAハンドルバー、口ひげ、小枝...を可能にする
ウィル

回答:


2

Shiny HTMLタグ(またはhtmltoolsタグ)を生成する関数を使用して、構成可能で再利用可能なUI要素を作成するのが好きです。あなたのサンプルアプリから、「ページ」要素を特定し、次に2つの汎用コンテンツコンテナーを特定して、それらの関数をいくつか作成します。

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」/「tool」だけです。

より複雑なデータがある場合や、より具体的な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タグを吐き出す関数です。テンプレートエンジンも動作する可能性がありますがhtmltools、Rのフルパワー+ を既に取得している場合は、特に必要はありません。


答えてくれてありがとう!私も以前はこのようにしていたのですが、HTMLの多くを再利用できないと、かなり非現実的になります。なんらかのテンプレートエンジンが唯一の実行可能な解決策になると思います:/
コンフォートイーグル

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は良いアイデアだと思いますが、もう1つの問題は、不要な空白です:https : //github.com/rstudio/htmltools/issues/19#issuecomment-252957684


ご入力いただきありがとうございます。コードはよりコンパクトになりますが、htmlとロジックの混合の問題は残ります。:/
コンフォートイーグル、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.