2

I am writing a shiny app that I want to deploy that produces a "rmarkdown document" for a given firm "on the fly". My Rmarkdown template takes Ticker as a parameter, filters the data, produces a table using kableExtra, a table using DT, a plot using ggplot, and a plot using ggplotly. Everything works fine when I run the template. When I embed the template into a shiny app following this approach:

https://davidruvolo51.github.io/shinytutorials/tutorials/rmarkdown-shiny/#:~:text=Rmarkdown%20is%20perfect%20for%20shiny,server%20into%20a%20Rmd%20template.

The kableExtra and the ggplot work, bu the DT and ggplotly are simply "not there". Can someone help? Suppose I would very much like to use both DT and ggplotly in this context.

Here is my "global.R" file:

tickers = c("AAPL","ATVI","AMZN","AON","AXP","BAC","BK","BYDDF",
                  "C","CE","CHTR","CVX","DEO","DVA","FND","FWONK","GL","GM","HPQ",
                  "ITOCF","JEF","JNJ","KHC","KO","KR","LILA","LILAK","LPX","LSXMA","LSXMK",
                  "MA","MCK","MCO","MDLZ","MKL","MMC","NU","OXY","PARA","PG",
                  "RH","SNOW","STNE","TMUS","TSM","UPS","USB","V","VRSN")

df  <- data.frame(tickers = rep(tickers,each=10),year = rep(11:20,length(tickers)), returns = rnorm(10*length(tickers)) )

library(dplyr)
library(shiny)
library(kableExtra)
library(ggplot2)
library(DT)
library(plotly)

Here is my template which is an .Rmd file. I marked the start and end of the code blocks with #``` so that the whole thing would look like code in this context. To get it to run you will need to delete the #.

---
title: "Test running an Rmarkdown Report via a Shiny App"
output: html_document
runtime: shiny


params: 
    ticker: MCO
---
 
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, eval=TRUE, message=FALSE, warning=FALSE)
        ticker007 = params$ticker

```

You have selected `r params$ticker`

print the firm

```{r}
filtered = df[df$ticker==ticker007,]
print(filtered)
```

# Test kableExtra:

```{r,fig.fig.align='center'}
kableExtra::kable(filtered) %>% kable_styling()
```


# Test DataTable:
```{r}
DT::datatable(filtered,filter = "none", options = list(pageLength=20,dom = "tip", bFilter = 0, bSort = 0, bProcessing = 0, bPaginate = 0, bInfo = 0,rownames=F),class=list(class="row-border")) %>%  DT::formatStyle(names(filtered),backgroundColor = "white")  
```


# Test ggplot:

```{r}
plot1 <- ggplot(filtered,aes(x=year,y=returns)) + geom_path()
plot1
```

# Test ggplotly:

```{r}

 ggplotly(plot1)
```

And this is my app:

source('global.R')

# Define UI for application that draws a histogram
ui <- fluidPage(
  selectInput(
    'ticker', 'Select ticker',
    choices = tickers ),
  htmlOutput('report')
)

server <- function(input, output) {
  output$report <- renderUI({
    includeHTML(
      rmarkdown::render(
        'testTemplate.Rmd',
        params = list(ticker = input$ticker)
      )
    )
  })
}

# Run the application 
shinyApp(ui = ui, server = server) 
r2evans
  • 141,215
  • 6
  • 77
  • 149
  • 2
    A slightly better way to allow rmarkdown docs in the Stack interface is to use the indent-style for code blocks: notice my suggested edit. By highlighting the rmarkdown text and pressing `Ctrl-K`, it indented it and allows all triple-backtick code chunks to be correct in this context. – r2evans Jun 12 '23 at 12:53
  • 1
    related: https://github.com/rstudio/shiny/issues/2535 and https://stackoverflow.com/q/40185986/3358272 – r2evans Jun 12 '23 at 13:00

2 Answers2

0

Not a good answer but too long for a comment.

The problem is that your Shiny app does not include the necessary JavaScript files (the plotly library for example).

If you add a DT table and a plotly chart in your app, then this will work:

source("global.R")
# Define UI for application that draws a histogram
ui <- fluidPage(
  
  DTOutput("dtable"),
  plotlyOutput("plotly"),

  br(),
  tags$hr(),
  br(),
  
  selectInput(
    'ticker', 'Select ticker',
    choices = tickers ),
  htmlOutput('report')
)

server <- function(input, output) {
  output$dtable <- renderDT({
    datatable(iris[1:5, ])
  })
  
  output$plotly <- renderPlotly({
    p <- ggplot(mtcars, aes(wt, mpg))
    p + geom_point()
  })
  
  output$report <- renderUI({
    includeHTML(
      rmarkdown::render(
        'testTemplate.Rmd',
        params = list(ticker = input$ticker)
      )
    )
  })
}

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

But you also have to use html_fragment as output type of the Rmd, otherwise jQuery is included two times and that's bad:

---
title: "Test running an Rmarkdown Report via a Shiny App"
output: html_fragment
runtime: static
params: 
    ticker: MCO
---

Of course you don't want to add a DT table and a plotly chart in your app. One solution is to hide them (with the CSS display: none). Or maybe you can render an empty DT table and an empty plotly chart...

Another, better solution would be to include the JavaScript libraries with htmltools::htmlDependency.

Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • How would htmltools::htmlDependency work? – DashdotdotDashdotdot Jun 12 '23 at 13:52
  • @DashdotdotDashdotdot The problem with the `htmlDependency` method is that one has to know which JS files (and maybe CSS fiies) included in the package must be included. The easisest way to know would be to make a plotly chart and to look at its dependencies. – Stéphane Laurent Jun 12 '23 at 14:02
0

I think You may omit the shiny application. In Your markdown You use shiny runtime, so what You need is to put the select input directly into the rmarkdown file and then use shiny::render* functions to render the outputs:

    ---
    title: "Test running an Rmarkdown Report via a Shiny App"
    output: html_document
    runtime: shiny
    ---
      
    ```{r setup, include=FALSE}
    knitr::opts_chunk$set(echo = FALSE, eval=TRUE, message=FALSE, warning=FALSE)
    ```
    
    ```{r}
    library(shiny)
    library(DT)
    library(ggplot2)
    library(plotly)
    ```
    
    
    ```{r}
    tickers = c("AAPL","ATVI","AMZN","AON","AXP","BAC","BK","BYDDF",
                      "C","CE","CHTR","CVX","DEO","DVA","FND","FWONK","GL","GM","HPQ",
                      "ITOCF","JEF","JNJ","KHC","KO","KR","LILA","LILAK","LPX","LSXMA","LSXMK",
                      "MA","MCK","MCO","MDLZ","MKL","MMC","NU","OXY","PARA","PG",
                      "RH","SNOW","STNE","TMUS","TSM","UPS","USB","V","VRSN")
    
    df  <- data.frame(
      tickers = rep(tickers,each=10),
      year = rep(11:20, length(tickers)),
      returns = rnorm(10*length(tickers))
    )
    
    shiny::selectInput(
      'ticker', 'Select ticker',
      choices = tickers )
    ```
    
    
    ```{r}
    filtered_data <- eventReactive(input$ticker, {
      filtered = df[df$ticker == input$ticker,]
    })
    ```
    
    ```{r}
    DT::renderDataTable({
      DT::datatable(filtered_data())
    })
    
    ```
    
    ```{r}
    renderPlotly({
      plot1 <- ggplot(
        filtered_data(),
        aes(x=year,y=returns)
    ) + geom_path()
      
      plotly::ggplotly(plot1)  
    })
    ```

Please, see this reference https://bookdown.org/yihui/rmarkdown/interactive-documents.html#intro-shiny

I know that this is not exactly what You were asking for, but it's hard for me to see the reason why You're trying to combine shiny and rmarkdown this way. Maybe with a little bit more context it will be clear.

Jakub Małecki
  • 483
  • 4
  • 14
  • With the above, I can produce a report for any of say 500 firms on my computer. If I can embed the report into a shiny app, I can publish the app, and anyone can run the report for any of the 500 firms from anywhere in the world. That's why I am trying to combine the two. (see https://rart.shinyapps.io/Dougs_Rmarkdown_App/) The reason why I am not just writing a shiny app is that an Rmarkdown document is more suited to writing something that looks like a report versus a dashboard or a UI. Is that clear? – DashdotdotDashdotdot Jun 15 '23 at 11:33
  • I understand. But do You know that You can publish the rmarkdown-based solutions and serve it in a similar way You'd do with a shiny app? Just dockerize it. Although this is a separate discussion I encourage You to explore the topic – Jakub Małecki Jun 15 '23 at 12:03