Rmarkdown: shiny DT server context in knit_child environment not rendering - r

I have a parent-child Rmarkdown file and I am trying to embed a Shiny UI-server structured DT table in the child rmd file. But the DT item won't render in child(but if put in parent, it will). When inspecting the HTML output, error message in dom saying:
shinyapp.js:342 Uncaught Duplicate binding for ID table_diamond
favicon.ico:1 Failed to load resource: the server responded with a status of
404 (Not Found)
Below is the sampled code I have:
Parent.Rmd:
---
title: "Hello Prerendered Shiny"
output:
html_document:
fig_caption: yes
keep_md: no
number_sections: no
theme: cerulean
toc: yes
toc_depth: 5
toc_float:
collapsed: true
runtime: shiny_prerendered
---
```{r setup, results=FALSE, eval=TRUE, echo=FALSE, message=FALSE, warning=FALSE}
library(DT)
library(tidyverse)
library(knitr)
library(c3)
```
## Content Listed by diamond color
```{r echo=FALSE, eval=TRUE, include=FALSE, warning=FALSE}
color <- levels(diamonds$color)
out <- NULL
for (c in color){
colorNum <- 0
for (ct in 1: length(levels(diamonds[diamonds$color== c, ]$cut ))) {
this_cut <- levels(diamonds[diamonds$color==c, ]$cut)[ct]
env = new.env()
out <- c(out, knit_child('sample_child.Rmd', envir = env))
colorNum <- colorNum +1
}
}
```
`r paste(out, collapse='\n')`
Child Rmd:
---
output: html_document
runtime: shiny_prerendered
---
```{r eval='TRUE', echo=FALSE, results='asis'}
if(colorNum ==0) cat('\n##',c,'\n'); #cat('\n');
```
### `r this_cut`
#### Price range on fixed color and cut
```{r eval=TRUE, echo=FALSE, fig.retina=1, dpi = 72,results='asis', warning=FALSE}
data <-subset(diamonds, color == c) %>%
filter(cut == this_cut) %>%
as.data.frame()
plot(x = data$clarity, y = data$price, ylab = 'Price', xlab = 'clarity')
```
#### Detail Table
```{r, echo=FALSE}
DT::dataTableOutput("table_diamond")
submitButton("Save")
```
```{r, context="server"}
output$table_diamond <- DT::renderDataTable({
data <-subset(diamonds, color == c) %>%
filter(cut == this_cut) %>%
as.data.frame()
datatable(data)
})
```
Any insights?

Figured out why:
Just as dom error said “shinyapp.js:342 Uncaught Duplicate binding for ID table_diamond”,the loop is creating output dataTable using the same output ID "table_diamond".
To make this output Id dynamic, in UI:
table_id <- paste0('table_', c, this_cut)
dataTableOuput(outputId = table_id)
in Server, use double square brackets [[ ]] instead of $:
output[[table_id]] <- DT::renderDataTable({
data <-subset(diamonds, color == c) %>%
filter(cut == this_cut) %>%
as.data.frame()
datatable(data)
})
Thanks to R Shiny dynamic tab number and input generation

Related

Is there a way to export multiple gt tables to a single HTML output

I have a list of many (can be dozens of) tables created with the gt package in R. I would like to export them all as a single HTML file with a little space between each table. Currently, I have been exporting each table individually, reading them into an RMarkdown with the xfun package, and knitting to a single HTML file. I'm curious if there is a method to cut out the intermediate step of saving each table individually and reading into the Rmd.
Example:
library(gt)
library(tidyverse)
tbl_list <- list(mtcar_tbl = mtcars %>% gt(),
iris_tbl = iris %>% gt(),
cars_tbl = cars %>% gt())
purrr::map(seq_along(tbl_list), function(rownum){
htmltools::save_html(html = tbl_list[[rownum]],
file = paste0("test",rownum,".html"))
})
RMarkdown to combine and export tables:
---
title: ""
output: html_document
---
```{r, echo=FALSE,message=FALSE,warning=FALSE}
library(xfun)
```
```{r echo=FALSE}
htmltools::includeHTML("test1.html")
```
<br><br>
```{r echo=FALSE}
htmltools::includeHTML("test2.html")
```
<br><br>
```{r echo=FALSE}
htmltools::includeHTML("test3.html")
```
One option would be to use purrr::walk with chunk option results="asis" to directly print your tables without the intermediate step. To add the line breaks after each table use cat("<br><br>"):
Note: For the reprex I just print the head of each table.
---
title: "Untitled"
output: html_document
date: "2022-09-19"
---
```{r echo=FALSE, results='asis', warning=FALSE, message=FALSE}
library(gt)
library(tidyverse)
tbl_list <- list(mtcar_tbl = mtcars,
iris_tbl = iris,
cars_tbl = cars)
tbl_list <- purrr::map(tbl_list, ~ head(.x) %>% gt() )
purrr::walk(tbl_list, function(x) { print(x); cat("<br><br>") })
```

r - flexdashboard isolate table for child Rmd

I'm trying to incorporate an Rmd I have been using into a flexdashboard. I'm curious if it is possible to isolate an uploaded file and use it as-is rather than writing a bunch of reactive functions. If this is my template, is it possible to get a static object named df that the child document can go ahead and run with?
---
title: "help"
runtime: shiny
output:
flexdashboard::flex_dashboard:
orientation: columns
---
```{r}
fileInput("data", "select data")
df <- isolate(input$data)
```
```{r, child="some_code.Rmd"}
```
My real example does something completely different but let's say some_code.Rmd looks like this:
---
title: "some code"
output: html_document
---
```{r packages, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message = FALSE)
library(tidyverse)
```
The data looks like this:
```{r}
as_tibble(df)
```
The numeric data can be summarized with the following means
```{r}
df |>
summarise(across(where(is.numeric), mean)) |>
gather()
```
This ended up working:
knitr::knit() + markdown::markdownToHTML() + HTML() ---> renderUI()
---
title: "help"
runtime: shiny
output:
flexdashboard::flex_dashboard:
orientation: rows
---
Sidebar {.sidebar}
==============================================
```{r file-input}
fileInput("data", "select data")
```
Row
==============================================
```{r knit-child}
observeEvent(input$data, {
df <- isolate(read.csv(input$data$datapath))
new_env <- list2env(list(df = df))
output$res <- renderUI({
knitr::knit("some_code.Rmd", quiet = TRUE, envir = new_env) |>
markdown::markdownToHTML() |>
HTML()
})
})
uiOutput("res")
```

Printing any number of dataframes stored in list as paged tables in rmarkdown

I often want to print out the dataframes contained in a list as paged tables in my rmarkdown documents. Calling each dataframe individually renders the desired ouptut if the right df_print option is selected. However, the point of having a list is that the number of dataframes varies depending on the parameters passed to the rmarkdown document; so that's no real solution.
Based on Vincent Guyader's answer to this question and on this example of rmarkdown::paged_table, I've tried to do the following without success.
Is there a way to achieve this at all? I'd be happy to use any package that supports pagination remotely resembling the df_print option.
---
title: "Printing paged tables from a list of dataframes in Rmarkdown"
output:
html_document:
df_print: paged
---
```{r}
library(DT)
library(rmarkdown)
library(purrr)
library(knitr)
df_list <- list("cars" = mtcars, "flowers" = iris)
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, results='asis')
```
### Desired output but impossible to generalise
```{r}
df_list[["cars"]]
```
```{r}
df_list[["flowers"]]
```
### datatable shows as blanks on the page
```{r}
map(df_list, ~DT::datatable(.x) %>%
htmltools::tagList() %>%
print())
```
### rmarkdown outputs dataframe contents as one very long string
```{r}
map(df_list, rmarkdown::paged_table)
```
The issue is that the JS dependencies needed to render the Datatable are not included in the HTML output. A workaround which I borrowed from here is to add a code chunk
```{r init-step, include=FALSE}
DT::datatable(mtcars)
```
outside of the loop or map statement which ensures that the JS dependencies are included. Also, I would recommend to switch to purrr::walk as using map has the effect that the tables are plotted twice.
---
title: "Printing paged tables from a list of dataframes in Rmarkdown"
output:
html_document:
df_print: paged
---
```{r}
library(DT)
library(rmarkdown)
library(purrr)
library(knitr)
df_list <- list("cars" = mtcars, "flowers" = iris)
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, results='asis')
```
### Desired output but impossible to generalise
```{r}
df_list[["cars"]]
```
```{r}
df_list[["flowers"]]
```
### datatable shows as blanks on the page
```{r init-step, include=FALSE}
DT::datatable(mtcars)
```
```{r}
walk(df_list, ~DT::datatable(.x) %>%
htmltools::tagList() %>%
print())
```
When using results='asis' argument, the renderer (here DT) has to be initialized once before being applied on a asis list.
This seems to be a general problem, see here with leaflet, and here with Highcharter.
The answer to this general question has been given here.
In this case:
---
title: "Printing paged tables from a list of dataframes in Rmarkdown"
output:
html_document:
df_print: paged
---
```{r,}
library(DT)
library(rmarkdown)
library(purrr)
library(knitr)
df_list <- list("cars" = mtcars, "flowers" = iris)
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, results='asis')
# initialize the renderer
data.frame() %>%
DT::datatable() %>%
knitr::knit_print() %>%
attr('knit_meta') %>%
knitr::knit_meta_add() %>%
invisible()
```
```{r , results='asis'}
#Remove already printed element and print the rest
df_list[[1]] <- NULL
map(df_list, ~DT::datatable(.x) %>%
htmltools::tagList() %>%
print())
```

How to get textOutput working when using Slidy with Shiny runtime

The following code has satisfying results locally But when uploaded on shinyapps.io does not work.
---
title: "shiny slidy app"
author: "IMI"
date: "11/29/2018"
output:
slidy_presentation:
self_contained: yes
runtime: shiny
---
```{r data, echo=FALSE, message=FALSE, warning=FALSE, paged.print=FALSE}
library(shiny)
data<- data.frame(Year= 1990:1999)
```
## First
```{r slideselect, echo=T, message=FALSE, warning=FALSE, paged.print=FALSE}
sliderInput("year", "Year",
min = min(data$Year), max = max(data$Year),
value = c(min(data$Year),max(data$Year)))
```
```{r print, echo=T}
year<-reactive(input$year)
output$rendtext<-renderText( year()[1]:year()[2])
textOutput("rendtext")
```
shinyapps.io:
local:
Any suggestion?
I just tried your code and it worked well:
I made the following steps in Rstudio:
1) Create a new Rmd file
---
title: "shiny slidy app"
author: "IMI"
date: "11/29/2018"
output:
html_document:
df_print: paged
slidy_presentation:
self_contained: yes
runtime: shiny
---
```{r data, echo=FALSE, message=FALSE, warning=FALSE, paged.print=FALSE}
library(shiny)
data<- data.frame(Year= 1990:1999)
```
## First
```{r slideselect, echo=T, message=FALSE, warning=FALSE, paged.print=FALSE}
sliderInput("year", "Year",
min = min(data$Year), max = max(data$Year),
value = c(min(data$Year),max(data$Year)))
```
```{r print, echo=T}
year<-reactive(input$year)
output$rendtext<-renderText( year()[1]:year()[2])
textOutput("rendtext")
```
2) Publish it on shinyapp.io via the dedicated button
Also I tried with a basic shiny deployment (looked at the shiny guidelines) and with this it's ok as well: it runs both local and on shinyapp.io.
# Global variables can go here
library(shiny)
data <- data.frame(Year= 1990:1999)
# Define the UI
ui <- bootstrapPage(
# Input: Simple integer interval ----
sliderInput("year", "Year", min = min(data$Year), max = max(data$Year), value = c(min(data$Year),max(data$Year))),
# Output: Text output summarizing the values ----
textOutput("rendtext")
)
# Define the server code
server <- function(input, output) {
# Reactive expression for the input values ---
year <- reactive(input$year)
# Show the values ----
output$rendtext<-renderText( year()[1]:year()[2])
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)

how to use a for loop in rmarkdown?

Consider this simple example:
---
title: "Untitled"
output: ioslides_presentation
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
## Slide with R Output
```{r t, warning=FALSE, message=FALSE}
library(knitr)
library(kableExtra)
library(dplyr)
for(threshold in c(20, 25)) {
cars %>%
filter(dist < threshold) %>%
kable('html') %>%
kable_styling(bootstrap_options = "striped")
}
```
Here I simply want to print each output of the for loop into a different slide. In this example, there are two calls to kablethat should go on two different slides.
The code above does not work. Am I even using the right packages for that? Any ideas?
Thanks!
You can use the asis option:
---
title: "Untitled"
output: ioslides_presentation
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
library(knitr)
library(kableExtra)
library(dplyr)
# needed so r will include javascript/css dependencies needed for striped tables:
kable(cars, "html") %>% kable_styling(bootstrap_options = "striped")
```
```{r, results = "asis"}
for (threshold in c(20, 25)) {
cat("\n\n##\n\n")
x <- cars %>%
filter(dist < threshold) %>%
kable('html') %>%
kable_styling(bootstrap_options = "striped")
cat(x)
}
```
To get rid of that bogus table, you can try to put options(kableExtra.html.bsTable = T) in your setup section.
Here's the start of a solution. You can print strings with markdown, either by making the strings yourself or using pander's pandoc.* functions. If you set results="asis" for that chunk, it will get compiled the same as any other markdown. I used cat to make the ## headings, but commented out two pander functions that you could try also to make headers or horizontal rules to split slides.
There's more detail on the pander functions here, plus other SO questions such as this one.
---
title: "Untitled"
output: ioslides_presentation
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
library(knitr)
library(kableExtra)
library(dplyr)
```
```{r, results='asis'}
for(threshold in c(20, 25)) {
# pander::pandoc.header(sprintf("Threshold = %s", threshold))
# pander::pandoc.horizontal.rule()
cat(paste("\n##", "Threshold =", threshold), "\n")
tbl <- cars %>%
filter(dist < threshold) %>%
kable(format = "html") %>%
kable_styling(bootstrap_options = "striped")
print(tbl)
}
```
One issue is that when I knit this, I'm not getting the striped table that you'd expect. If I add a slide before this chunk and put a table in it with these kableExtra settings, I do get stripes, but the first table is also pretty ugly...I'm not sure if that's a bug or conflicting CSS somewhere or what.

Resources