<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/printing-from-flex-dashboard/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/"> R Views</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>Shiny applications of all stripes (including <a rel="nofollow" href="http://rmarkdown.rstudio.com/flexdashboard/shiny.html" target="_blank">flexdashboard with runtime Shiny</a>) are revolutionary in that they put the power of R directly in the end user’s hands without needing to interact directly with the language. A common way end-users wish to interact with their data is via a dashboard that they can manipulate on the fly. <a rel="nofollow" href="http://rmarkdown.rstudio.com/flexdashboard/" target="_blank">Flexdashboard</a> streamlines the process of turning an R-based analysis into a dashboard, so R users can create good-looking output for their analyses – and deploy this output to the web – with very little additional effort.</p>
<p>There are various ways to extract information from flexdashboard, but no good way to programatically return a printable version of the <em>dashboard itself</em>. This is partially by design – responsive HTML dashboards aren’t meant to be printed. There are <a rel="nofollow" href="https://shiny.rstudio.com/articles/generating-reports.html" target="_blank">well-documented workflows</a> about how to share information between a web-centric output and a complementary report that renders to a printable format.</p>
<p>That said, there are some reasons to want a printable output that looks similar to a related HTML report. Snapshots are valuable for documentation/training materials, for embedding in emails or other reports, or for the all-too-common case of meetings with executives who like dashboards but want to consume paper-based reports. Plus, flexdashboards are an incredibly quick way to create visually appealing <em>(read: pretty)</em> summary output of a combination of outputs on a single page.</p>
<p>Both Harrison and Aaron<a rel="nofollow" href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/#fn1" target="_blank"><sup>1</sup></a> have struggled with trying to print flexdashboards in their respective practices. In a recent instance, Harrison was working with a client whose end users would be carrying snapshots of the dashboard into an industrial setting where mobile devices were not desired. It was important to the client to maintain a similar look between the on-screen and paper representations of the dashboard. It was also important to make generating these snapshots an integrated part of the application; it was not sufficient to suggest that the client manually take a screenshot of their browser window. This turned out to be a blessing, because (as you will see below) it allows the creation of a web-friendly ‘tabbed’ report with a companion ‘flat’ printed rendering.</p>
<div>
<h3>Specific Task</h3>
<p>Our goal was to create a “printable” flexdashboard with runtime Shiny. Flexdashboard produces a beautiful HTML dashboard with very little effort. <a rel="nofollow" href="https://shiny.rstudio.com/articles/interactive-docs.html" target="_blank">Like all R Markdown documents</a>, it also allows <a rel="nofollow" href="https://shiny.rstudio.com/" target="_blank">Shiny</a> objects to be embedded inside of it, so that when rendered locally or on a server, it produces a reactive web application with minimal developer effort. Like many HTML files, however, it does not print well.</p>
<p>The flexdashboard in question also had tabsets, which make perfect sense on a computer or mobile device, but don’t translate well to printed media. When printed, tabsets communicate that there is pertinent information hidden from the viewer; better to remove the tabset in the printed version. If the information is pertinent, it should be displayed in the printed document; if it is not, it should be removed.</p>
</div>
<div>
<h3>PhantomJS and Headless Chrome</h3>
<p>The approach we settled on as <em>good enough and easy enough</em> was to use a <a rel="nofollow" href="https://en.wikipedia.org/wiki/Headless_browser" target="_blank">headless browser</a> to take a screenshot of a non-reactive HTML document. It’s not a perfect solution: a PDF would be better than a png file, headless browsers add a code dependency, and there is some administrative overhead to create the document to print. It is, however, quick to implement, easy to understand, and robust.</p>
</div>
<div>
<h3>Example<a rel="nofollow" href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/#fn2" target="_blank"><sup>2</sup></a></h3>
<p>The mechanics of building a screen-capture-style report from flexdashboard are neither difficult nor obvious. In general, it requires the following steps:</p>
<ol>
<li>Add a downloadButton to a <a rel="nofollow" href="http://rmarkdown.rstudio.com/flexdashboard/shiny.html" target="_blank">flexdashboard with runtime Shiny</a></li>
<li>When the download button is invoked, temporarily save non-reactive copies of any objects of interest</li>
<li>Knit a static HTML version of the flexdashboard, using the objects saved in the prior step</li>
<li>Use <a rel="nofollow" href="https://github.com/wch/webshot" target="_blank"><code>webshot::webshot</code></a> or <a rel="nofollow" href="https://github.com/hrbrmstr/decapitated" target="_blank"><code>decapitated::chrome_shot</code></a> to capture a .png image of the static dashboard<a rel="nofollow" href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/#fn3" target="_blank"><sup>3</sup></a></li>
<li>Deliver this version to the client using the downloadHandler function</li>
</ol>
<p>The solution requires two separate R documents. The process is as follows:</p>
<div>
<h4>Dynamic Flexdashboard document</h4>
<pre><code>
---
title: &quot;Snapshot Example&quot;
runtime: shiny
output:
  flexdashboard::flex_dashboard:
    orientation: rows
    vertical_layout: scroll
---
</code></pre>
<p>This is standard YAML header for a flexdashboard, including the command <code>runtime: shiny</code> to create a Shiny application from an R Markdown document. Let’s examine the next chunks:</p>
<pre><code>gears = mtcars$gear %>% as.factor() %>% levels()
selectizeInput(&quot;grs&quot;, &quot;GEARS&quot;,
               gears, selected = gears[1])
renderUI({
downloadButton(&quot;downloadFile&quot;, &quot;Download&quot;)
})</code></pre>
<p>In this chunk, we have built a <code>selectizeInput</code> object to choose the number of gears to select, as well as a <code>downloadButton</code> named ‘downloadFile’. Notice that we have wrapped the button in a <code>renderUI()</code> context. This is standard shiny / flexdashboard code.</p>
<pre><code>mpgp = reactive({
  mtcars %>% filter(gear %in% input$grs) %>% ggplot(aes(x = hp, y = mpg)) + geom_point() + geom_smooth()
  
})
renderPlot({
  mpgp()
})
</code></pre>
<p>For veteran shiny programmers, this block may seem redundant. We’re creating a plot called <code>mpgp</code> in a reactive context and then rendering it. In a ‘standard’ shiny implementation, we would simply have wrapped the plot code in the <code>renderPlot()</code> context. However, as you will see below, we’re going to need to reuse the object <code>mpgp</code>.</p>
<pre><code>output$downloadFile &lt;- downloadHandler(filename = function() { 
return(paste('Cars', '.png', sep=''))
},
    content = function(file){
    to_save &lt;- list(
    mpgg = mpgg(),
    mpgp = mpgp()
    )
      saveRDS(to_save, &quot;config_data.RDS&quot;)
      rmarkdown::render(&quot;ShadowCars.Rmd&quot;)
      webshot::webshot(&quot;ShadowCars.html&quot;, file = file)
                        })</code></pre>
<p>This chunk contains the secret sauce. When the <code>downloadButton</code> is invoked, the program saves all elements to appear in the downloaded report in a file called ‘config_data.RDS’. These elements are then used as arguments to render a shadow document, ‘ShadowCars.Rmd’. Finally, <code>webshot::webshot</code> converts the hidden document to a .png and delivers it to the client side.</p>
<p>The full dashboard looks like this:</p>
<p></p>
<p>[]](/post/2017-06-19-Printing-From-Flex-Dashboard_files/MinDWN.png)</p>
</div>
<div>
<h4>Shadow Document</h4>
<p>The key elements of the shadow document code are presented below:</p>
<pre><code>---
title: &quot;Snapshot Example&quot;
output:
  flexdashboard::flex_dashboard:
    orientation: rows
---</code></pre>
<p>Note that our YAML for the shadow document is similar to the dashboard, except it does <strong>not</strong> include <code>runtime: shiny</code></p>
<p>The code blocks that render the objects created in the dynamic document are straightforward to the point of triviality; they simply reference the saved objects (which are retrieved using <code>readRDS</code>) and render them using whatever we choose, in this case <code>kable()</code> for the table and the native <code>print()</code> for the plot.</p>
<pre><code>data &lt;- readRDS(&quot;config_data.RDS&quot;)
data$mpgg %>% kable() </code></pre>
<pre><code>data$mpgp %>% print() </code></pre>
<p>And of course, while <code>mpgp</code> was reactive in the original dashboard, it is static in the shadow doc.</p>
<p>The report delivered to the client is as follows:</p>
<p></p>
<div>
<img src="https://i0.wp.com/rviews.rstudio.com/post/2017-06-19-Printing-From-Flex-Dashboard_files/Cars.png?w=456&ssl=1" data-recalc-dims="1">
</div>
</div>
</div>
<div>
<h2>Conclusion</h2>
<p>In this short post, we have shown how to create a downloadable custom report by using existing tools to create a static flexdashboard. From the .png environment, the rendered image can be converted to other formats as required.</p>
</div>
<div>
<hr>
<ol>
<li>
<p><a rel="nofollow" href="https://www.linkedin.com/in/harrisonschramm" target="_blank">Harrison Schramm</a> is an Operations Research Analyst at <a rel="nofollow" href="http://www.canallc.com/" target="_blank">CANA Advisors</a>. <a rel="nofollow" href="https://www.linkedin.com/in/aaron-berg-26150917/" target="_blank">Aaron Berg</a> is a Customer Success Representative at RStudio.<a rel="nofollow" href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/#fnref1" target="_blank">↩</a></p>
</li>
<li>
<p>To recreate this minimal example for yourself, please download the files from <a rel="nofollow" href="https://gist.github.com/agberg/d71d3aacb9b9d5691b7ba7ab7555ba5b" target="_blank">this gist</a>.<a rel="nofollow" href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/#fnref2" target="_blank">↩</a></p>
</li>
<li>
<p>The <a rel="nofollow" href="https://github.com/wch/webshot" target="_blank"><code>webshot</code></a> package requires phantomJS to be installed as an external dependency, but will work on all systems. <a rel="nofollow" href="https://github.com/hrbrmstr/decapitated" target="_blank"><code>decapitated</code></a> will only work on systems with versions of Chrome that can operate in <a rel="nofollow" href="https://developers.google.com/web/updates/2017/04/headless-chrome" target="_blank">headless mode</a>.<a rel="nofollow" href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/#fnref3" target="_blank">↩</a></p>
</li>
</ol>
</div>
<p> </p>
	<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://rviews.rstudio.com/2017/06/28/printing-from-flex-dashboard/"> R Views</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a rel="nofollow" href="https://www.r-project.org/" title="The R Project for Statistical Computing">R</a> news and <a rel="nofollow" href="https://www.r-bloggers.com/search/tutorial" title="R tutorials">tutorials</a> on topics such as: <a rel="nofollow" href="https://www.r-bloggers.com/search/data%20science" title="Data science">Data science</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data" title="Big Data">Big Data, <a rel="nofollow" href="https://www.r-users.com/" title="R jobs">R jobs</a>, visualization (<a rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2" title="ggplot and ggplot2 tutorials">ggplot2</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/boxplot" title="Boxplots using lattice and ggplot2 tutorials">Boxplots</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/map" title="Maps and gis">maps</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/animation" title="Animation in R">animation</a>), programming (<a rel="nofollow" href="https://www.r-bloggers.com/search/RStudio" title="RStudio IDE for R">RStudio</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/sweave" title="Sweave and literate programming">Sweave</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX" title="LaTeX in R">LaTeX</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/SQL" title="SQL and databases">SQL</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/eclipse" title="Eclipse IDE for R">Eclipse</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/git" title="git and github, Version Control System">git</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/hadoop" title="Large data in R using Hadoop">hadoop</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping" title="Web Scraping of google, facebook, yahoo, twitter and more using R">Web Scraping</a>) statistics (<a rel="nofollow" href="https://www.r-bloggers.com/search/regression" title="Regressions and ANOVA analysis tutorials">regression</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/PCA" title="principal component analysis tutorial">PCA</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/time+series" title="Time series">time series</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/trading" title="finance trading">trading</a>) and more...
<img width="1" alt="" src="http://feeds.feedburner.com/~r/RBloggers/~4/VdUzTeI9bYA" height="1"></a></div>