Search code examples
rr-markdownreactable

R programmatically created reactable chunk won't show in Rmarkdown


I have this dataframe

products <- c("Product1", "Product2", "Product3")
departments <- c("Dept1", "Dept2", "Dept3")
campaigns <- c("Campaign1", "Campaign2", "Campaign3", "Campaign4", "Campaign5")
agents <- c("Agent1", "Agent2", "Agent3", "Agent4", "Agent5")

combinations <- expand.grid(Product = products, 
                            Department = departments, 
                            Campaign = campaigns, 
                            Agent = agents)

grouped_data <- combinations %>%
  group_by(Product, Department, Campaign, Agent) %>%
  summarize(Total_Sales = sum(Sales), 
            Average_Sales = mean(Sales))

I'd like to print in Rmarkdown programmatically without manually creating so many sections in my document so I created this function:

render_content <- function(dataframe) {
  split_data <- split(dataframe, f = list(dataframe$Product, dataframe$Campaign))
  
  for (product in unique(dataframe$Product)) {
    cat(paste0("<h2>", product, "</h2>\n"))
    product_data <- split_data[grepl(product, names(split_data))]
    
    for (campaign in unique(dataframe$Campaign)) {
      campaign_key <- paste(product, campaign, sep = ".")
      
      if (campaign_key %in% names(product_data)) {
        cat(paste0("<h3>", campaign, "</h3>\n"))
        table_html <- reactable(product_data[[campaign_key]], 
                                defaultPageSize = 5, 
                                minRows = 0)
        cat(as.character(htmltools::as.tags(table_html)))
      }
    }
  }
}

I put this chunk to my rmarkdown document but only h2 and h3 tags are rendered, I believe it has something to do with the necessity to include tags in documents so I tried several variants of it, but nothing helps.

{r, include=FALSE}
htmltools::tagList(reactable())

{r results='asis', echo=FALSE}
render_content(grouped_data)

Solution

  • Here is a reproducible example of one approach how you can achieve your aims. It uses a child document.

    The next part is your main .Rmd file.

    ---
    title: "A title"
    output: html_document
    ---
    
    ```{r setup, include=TRUE}
    knitr::opts_chunk$set(echo = TRUE)
    
    library(tidyverse)
    ## {reactable} used with namespace
    ```
    
    # Data
    
    ```{r}
    dat <- expand.grid(
      products = c(paste("prd", 1:3, sep = "_")),
      departments = c(paste("dept", 1:3, sep = "_")),
      campaigns = c(paste("camp", 1:3, sep = "_") ),
      agents = c(paste("agent", 1:3, sep = "_"))
      ) %>% 
      mutate(sales = rnorm(81, 5000, 500)) %>% 
      filter(!(products == "prd_1" & campaigns == "camp_3")) 
    ```
    
    Exclude the combination of product 1 and campaign 3 since it might be another
    interesting way to see that the headers/sections will be generated correctly.
    
    # Function(s)
    
    ```{r}
    func_render_content <- function(x_dat) {
      
      param_env <- new.env()
      param_env$x_dat <- x_dat
      
      for(product_i in unique(x_dat$products)) {
        cat(paste0("<h2>", product_i, "</h2>\n"))
        
        product_data <- x_dat %>% filter(products == product_i)
        
        for(campaign_i in unique(x_dat$campaigns)) {
          
          if(campaign_i %in% unique(product_data$campaigns)) {
            cat(paste0("<h3>", campaign_i, "</h3>\n"))
            
            param_env$product_i <- product_i
            param_env$campaign_i <- campaign_i
            
            cat(knitr::knit_child(
              'so-content.Rmd', 
              envir = param_env, 
              quiet = TRUE
              ))
            
            cat("\n\nThat's it on", product_i, "and", campaign_i, "end of sentence.")
          }
        }
      }
    }
    ```
    
    ```{r}
    func_render_content_your_attempt <- function(x_dat) {
      
      for (product_i in unique(x_dat$products)) {
        cat(paste0("<h2>", product_i, "</h2>\n"))
        
        product_data <- x_dat %>% filter(products == product_i)
        
        for (campaign_i in unique(x_dat$campaigns)) {
          
          if (campaign_i %in% unique(product_data$campaigns)) {
            cat(paste0("<h3>", campaign_i, "</h3>\n"))
            
            x_dat %>% 
              filter(products == product_i & campaigns == campaign_i) %>% 
              reactable::reactable(defaultPageSize = 5)
          }
        }
      }
    }
    ```
    
    # This works
    
    ```{r, results='asis'}
    func_render_content(dat)
    ```
    
    # Your attempt, does not what you want
    
    In the sense of completeness.
    
    ```{r, results='asis'}
    func_render_content_your_attempt(dat)
    ```
    
    

    The next part is a separate .Rmd file called so-content.Rmd.

    ---
    title: "section-content"
    ---
    
    ```{r, echo=FALSE}
    x_dat %>% 
      filter(products == product_i & campaigns == campaign_i) %>% 
      reactable::reactable(defaultPageSize = 5)
    ```