ふんわり R-tips

ぜんぜんわからない、俺たちは雰囲気でRをやっている

insertUIを用いた動的なShinyのユーザインタフェース

このエントリでは、rstudio::conf 2017で紹介されたShinyのinsertUIを解説します。

https://github.com/bborgesr/rstudio-conf2017

githubで公開されたソースコード(03-insertUI.R)を引用しています。

Shinyアプリケーションでは、予めui.R、または (app.R中の) uiオブジェクトとして、固定のUIを配置する必要があります。固定ではなく動的なUIを実現するために、 renderUIを用いて、ui中にuiOutputで指定したコンポーネントを動的に表示することができます。renderUIは便利ですが、新たにコンポーネントを追加する場合には、uiOutputで確保したスロットに再度render関数による表示が必要となります。

これを回避して、より動的にコンポーネントの追加表示を行うのに使用するのがinsertUIです。

insertUIの使用例

実行例

初期状態ではメインパネルにコンポーネントがありません。

f:id:phmpk:20170307191847p:plain

datasetがrock、toolがsummaryの状態でボタンを押すと、データrockのsummaryがメインパネルに追加されます。

f:id:phmpk:20170307192019p:plain

次に、datasetがpressure、toolがplotの状態でボタンを押すと、データpressureのplotが、最初に追加したsummaryの下側に追加されます。

f:id:phmpk:20170307192038p:plain

次に、datasetがcars、toolがheadの状態でボタンを押すと、データcarsの表がplotの下側に追加されます。

f:id:phmpk:20170307192159p:plain

ソースコード

library(shiny)
library(datasets)

shinyApp(
  ui = fluidPage(
    titlePanel('Using insertUI'),
    sidebarLayout(
      sidebarPanel(
        selectInput('data', 'Choose a dataset', c('rock', 'pressure', 'cars')),
        radioButtons('tool', 'Choose a tool', c('summary', 'plot', 'head')),
        actionButton('add', 'Add result')
      ),
      mainPanel(
        div(id = 'placeholder')
      )
    )
  ),
  server = function(input, output, session) {
    dataset <- reactive({ switch(input$data,
      'rock' = rock, 'pressure' = pressure, 'cars' = cars)
    })
    
    observeEvent(input$add, {
      id <- paste0(input$tool, input$add)
      insertUI('#placeholder', 
        ui = switch(input$tool,
          'summary' = verbatimTextOutput(id),
          'plot' = plotOutput(id),
          'head' = tableOutput(id))
      )
      output[[id]] <-
        if (input$tool == 'summary') renderPrint({ summary(isolate(dataset())) })
        else if (input$tool == 'plot') renderPlot({ plot(isolate(dataset())) })
        else if (input$tool == 'head') renderTable({ head(isolate(dataset())) })
    })
  }
)

ui側での定義

サイドバーにactionButtonを配置します。ボタンの押下されると、reactiveにinput$addに値が返されます。

actionButton('add', 'Add result')

メインパネルには、HTMLでid属性placeholderを指定して

タグを埋め込みます。

div(id = 'placeholder')

server側での定義

ボタン(input$add)に対応するイベントを記述します。以下が、最もシンプルなinput$addに対応したobserveEventの記述例です。

observeEvent(input$add, {
  insertUI(selector = "#placeholder",
           where = "afterEnd",
           ui = tagList(...))
})

insertUIの各引数では、

  • selector#placeholderでUIオブジェクトを挿入したいjQueryのselectorを指定

  • whereはデフォルト値のbeforeEndとなり、追加するUIオブジェクトは、最後に追加された子UIの後に挿入されます

  • uiで挿入したいUIオブジェクトを指定します。

observeEvent(input$add, {
  id <- paste0(input$tool, input$add)
  insertUI('#placeholder', 
    ui = switch(input$tool,
      'summary' = verbatimTextOutput(id),
      'plot' = plotOutput(id),
      'head' = tableOutput(id))
  )
  output[[id]] <-
    if (input$tool == 'summary') renderPrint({ summary(isolate(dataset())) })
    else if (input$tool == 'plot') renderPlot({ plot(isolate(dataset())) })
    else if (input$tool == 'head') renderTable({ head(isolate(dataset())) })
})

まとめ

  • insertUIを用いて、動的にUIコンポーネントを追加することができます。

  • uiでコンポーネントを追加していく場所をdivタグにidを指定して確保し、serverでボタン押下などの追加のユーザ入力イベントが起きたときにinsertUIを実行します。

  • renderUIと比較すると、複数のコンポーネントを配置したい場合に、再度全体をrenderし直す必要がなく、より動的なアプリケーションにできることが特徴です。