3

I am trying to embed a tutorial Rmd from the learnr package into a full shiny app. However, learnr uses the shiny_prerendered runtime, I cannot call it within my app. How do I get an interactive tutorial to run within my shiny app?

I have have three files right now: ui.R, server.R, and tutorial.Rmd.

My tutorial looks like this (one ` removed for formatting)

---
title: "my tutorial"
tutorial:
  id: "com.example.tutorials.a-tutorial"
  version: 1.0
output: learnr::tutorial
runtime: shiny_prerendered
---

``{r setup, include=FALSE}
library(learnr)
knitr::opts_chunk$set(echo = FALSE)
``

### Exercise Example
An R code question
``{r add-function, exercise=TRUE, exercise.lines = 5}
add <- function() {

}
``

### Quiz
R Quiz Question
``{r quiz}
quiz(
  question("Question 1",
    answer("wrong"),
    answer("also wrong"),
    answer("right", correct = TRUE),
    answer("wrong again")
  )
)
``

When I try rendering the output of this file from ui.R like so:

ui <- tagList(
    fluidPage(theme = shinytheme("cosmo")),
    navbarPage(
       "appTitle",
       tabPanel("Embedding Tutorials?", 
          includeMarkdown("tutorial.Rmd")
       ),
    )
)

It (properly, I believe) displays it as a regular old Rmd file, not an interactive tutorial.

I've also tried using rmarkdown::render("tutorial.Rmd") which just renders the filepath to the html file generated by the Rmd (/Users/me/app/tutorial.html).

When I try to render any tutorial using run_tutorial("hello", package="learnr"), it (again, rightfully) gives the error ERROR: Can't callrunApp()from withinrunApp(). If your application code containsrunApp(), please remove it.

I've already discovered that I can create question chunks using the question() function in learnr using the following:

ui <- tagList(
    fluidPage(theme = shinytheme("cosmo")),
    navbarPage(
       "appTitle",
       tabPanel("Tutorial", 
             quiz(
               question("Quiz question",
                        answer("1"),
                        answer("2"),
                        answer("3", correct = TRUE),
                        answer("4"),
                        allow_retry = TRUE
               )
       ),
    )
)

But this does not allow the functionality of creating R code chunks that can be run within the app.

What I want is a fully interactive learnr tutorial that can be rendered from within a ui.R file for a shiny app. Is this possible?

Carolyn
  • 93
  • 8
  • 1
    I don't know the `learnr` package so excuse me if I'm being dumb, but if the tutorial is pre-rendered can you not just embed it in your html with ` – Phil Apr 09 '19 at 15:51
  • 1
    when the header of `tutorial.Rmd` actually produces html (the header is set to `output: html_document`) it is no longer interactive (in the viewer/opening in browser). I don't think it would work to embed it as raw html in the shiny ui using something like what's found [here](https://shiny.rstudio.com/articles/html-tags.html). (But I am trying that now) – Carolyn Apr 09 '19 at 16:01
  • 1
    I think what I might need to do is just link to another shiny app, unfortunately. Instructions for that found [here](https://stackoverflow.com/questions/42349409/open-up-a-shiny-app-by-clicking-a-link-in-a-different-shiny-app) – Carolyn Apr 09 '19 at 16:16
  • 1
    Rather than embed the tutorial in another shiny app, can you not expand your tutorial to include the additional material? You could then publish it all as one shiny instance? https://rmarkdown.rstudio.com/authoring_shiny_prerendered.html and https://rstudio.github.io/learnr/publishing.html might be helpful? – Phil Apr 09 '19 at 20:43
  • 1
    I'm just concerned about the extent to which Rmd can do the formatting I'm looking for, but I think you're right, this is probably the way to go if I really want an embedded tutorial. – Carolyn Apr 10 '19 at 08:21

2 Answers2

1

As well as my suggestion to incorporate your extra material into the learnr tutorial I also got <iframe> embedding to work. Create an app.R with the following contents:

#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

library(shiny)

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("learnr tutorial"),

    # Show a plot of the generated distribution
    mainPanel(fluidRow(
       htmlOutput("frame")
    ))
)

# Define server logic required to draw a histogram
server <- function(input, output) {

    output$frame <- renderUI({
        tags$iframe(
            src="https://jjallaire.shinyapps.io/learnr-tutorial-03a-data-manip-filter/", width=1280, height=720
        )
    })
}

# Run the application
shinyApp(ui = ui, server = server)

Now when you Run App this should embed the example tutorial from https://rstudio.github.io/learnr/

It appears to be necessary for the tutorial to be rendered and published to shinyapps.io, etc.: I couldn't get it to work just from the rendered html file. So,

  1. Create tutorial
  2. Publish tutorial
  3. Embed tutorial

seems to be the way forward.

Phil
  • 4,344
  • 2
  • 23
  • 33
  • 1
    Thanks for looking into this! Sure enough that works like a charm. Only when the app is run in the browser, which if I understand the way Shiny apps are locally run when developing in RStudio also makes sense. Thanks again! – Carolyn Apr 10 '19 at 08:29
  • 1
    No worries. Please accept one of the answers so others can see it's resolved. Good luck – Phil Apr 10 '19 at 08:55
0

Generally speaking, there are two ways to embed interactive RMarkdown documents in shiny applications.

(1) The usual way (as proposed by @Phil) is to have one R server running the embedded tutorial and another one running the application. This can be archived by deploying the tutorial via shinyapps.io or shiny-server first and then using an iframe. Alternatively, you could use callr::r_bg() to run the tutorial in a local background process. In any case, this will make it so the Rmd document can not interact with the shiny application. If this is feasable for your usecase, I would suggest this option.

(2) A more convoluted way is outlined here by the maintainer of the shinyAce package. It uses the same R server for the main application and the embedded Rmd document. See also this SO question. AFAIK, this only works with knitr::knit2html which relies on an outdated version of RMarkdown. Also, the amount of render* and output* functions available in this manner is limited unless you make sure certain JavaScript and CSS resources are properly included in your ui definition.

Quite some time has passed since I wrapped my head around this topic but my impression at the time was, that (2) takes quite a lot of work and really limits your options.

Gregor de Cillia
  • 7,397
  • 1
  • 26
  • 43