このエントリでは、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の使用例
実行例
初期状態ではメインパネルにコンポーネントがありません。
datasetがrock、toolがsummaryの状態でボタンを押すと、データrockのsummaryがメインパネルに追加されます。
次に、datasetがpressure、toolがplotの状態でボタンを押すと、データpressureのplotが、最初に追加したsummaryの下側に追加されます。
次に、datasetがcars、toolがheadの状態でボタンを押すと、データcarsの表がplotの下側に追加されます。
ソースコード
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し直す必要がなく、より動的なアプリケーションにできることが特徴です。