Shiny Inception: JavaScript in Rendered Markdown

[This article was first published on R | datawookie, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

I’m busy helping a colleague with a Shiny application. The application includes HTML content rendered from a .Rmd document. However, there’s a catch: the .Rmd uses the {DT} package to render a dynamic DataTable. It turns out that this doesn’t immediately work because the JavaScript in the embedded document isn’t run.

I’ll use a simple document and application structure to illustrate the problem.

Static Document

Let’s start with a .Rmd document which renders two different static views of the Palmer Archipelago (Antarctica) Penguin Data.

This is what the rendered document looks like:

The following Shiny application embeds this document perfectly using includeHTML() to read in the HTML file and htmlOutput() to inject it into the UI.

library(shiny)
library(rmarkdown)

ui <- fluidPage(
    htmlOutput("document")
)

server <- function(input, output) {
    output$document <- renderUI({
        path_rmd <- "penguins-static.Rmd"
        path_html <- tempfile(fileext = ".html")
        render(
            path_rmd,
            output_file = path_html
        )
        includeHTML(path_html)
    })
}

shinyApp(ui = ui, server = server)

And this is what the application looks like. Precisely as expected.

Dynamic Document

Now, let’s replace the static document with a .Rmd document using the {DT} package to create a dynamic table. Simply knitting the document gives the output below, a responsive table with a selection of bells and whistles.

How about simply plugging this into the original Shiny application?

library(shiny)
library(rmarkdown)

ui <- fluidPage(
    htmlOutput("document")
)

server <- function(input, output) {
    output$document <- renderUI({
        path_rmd <- "penguins-dt.Rmd"
        path_html <- tempfile(fileext = ".html")
        render(
            path_rmd,
            output_file = path_html
        )
        includeHTML(path_html)
    })
}

shinyApp(ui = ui, server = server)

Let’s give that an optimistic whirl.

Doh! The table doesn’t appear at all. Why? I’m guessing that the viewer is using JavaScript to run the application but it doesn’t extend to running JavaScript in an embedded document.

To get this to work I used an <iframe>, which in retrospect is the obvious solution for embedding a complete HTML page into the application. Getting it to work though is not entirely trivial.

library(shiny)
library(here)
library(rmarkdown)

dir.create("www")

ui <- fluidPage(
    htmlOutput("document")
)

server <- function(input, output) {
    output$document <- renderUI({
        path_rmd <- "penguins-dt.Rmd"
        # Render into www/ folder.
        path_html <- tempfile(fileext = ".html", tmpdir = "www")
        render(
            path_rmd,
            output_file = path_html
        )
        tags$iframe(
            style = "border-width: 0;",
            width = "100%",
            height = 800,
            # Filename relative to the www/ folder.
            src = basename(path_html)
        )
    })
}

shinyApp(ui = ui, server = server)

There’s a bit of plumbing required: the document must be rendered into the www/ folder so that it’s visible to the application at runtime.

Here’s the result:

🚀 Success!

To leave a comment for the author, please follow the link and comment on their blog: R | datawookie.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)