Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating dynamic tabs in Rmarkdown

In Rmarkdown, it's possible to create tabs, for example:

---
output: html_document
---

# Tabs {.tabset}

## Tab 1

foo

## Tab 2

bar

I'm wondering if it's possible to create an arbitrary number of tags? How can I create a tab programatically?

The following code is a poor attempt to do this, but it results in a heading instead of a tab.

---
output: html_document
---

# Tabs {.tabset}

```{r echo=FALSE}
shiny::tags$h2("Tab 1")
```

foo

## Tab 2

bar

Solution

Thanks to @GGamba for providing a great solution. I needed to go one step further and be able to add tabs as part of a loop, so I needed to make two changes. First of all, I used this code to dynamically add tabs (the only difference here is that I force the evaluation of hrefCode inside the timeout because otherwise all timeouts called together will use the same value)

(function(hrefCode){setTimeout(function(){
 var tabContent = document.createElement('div');
 var tabContainerTarget = document.getElementsByClassName('tab-content')[0];

   tabContent.setAttribute('id', 'tab-' + hrefCode);
   tabContent.setAttribute('class', 'tab-pane')
   tabContent.innerHTML = '", gsub('\n', '', Panel, fixed = TRUE), "';

   tabContainerTarget.appendChild(tabContent);
   }, 100);
})(hrefCode);

Secondly, to add tabs in a loop, you can do something like this:

tabsToAdd <- list("tab3" = "hello", "tab4" = "world")

shiny::tagList(lapply(names(tabsToAdd), function(x) {
  addToTabset(title = x, tabsetId = 'tbSet1',
              tabPanel(x, tabsToAdd[[x]]))
}))
like image 695
DeanAttali Avatar asked Mar 06 '17 17:03

DeanAttali


People also ask

How do you make tabs in Rmarkdown?

To implement the tabbed section in an Rmarkdown document like the example above, all you have to do is putting {. tabset} on the right of a header. Then all the content below each sub-header will appear within a tab. Besides, this feature is only avaliable when rendering an Rmarkdown file to an html file.

How to end tabset in r markdown?

To end the tabset, you need to start a new section header of the upper level. The new section header can be empty, e.g., ## Results {. tabset} ### Tab One ### Tab Two ## {-} With the above unnumbered (`{-}`) and empty section header, we can end the tabset and continue to write more paragraphs.

How do you indent in R markdown?

Text can be indented two ways depending on if the indent is within a list or not. Within a list, four spaces at the begin of the line indicates the text is to be indented one nesting level. Use four additional spaces for each additional nesting level. To indent text which is not in a list, use a block quote.


2 Answers

There is also a simple rmarkdown solution to this problem that does not require shiny and/or custom javascript. Does not work for all kinds of R output (see below):

## Tabbed Example {.tabset}

```{r, results = 'asis'}
for (nm in unique(iris$Species)){
  cat("### ", nm, "\n")
  cat(knitr::knit_print(plot(iris[iris$Species == nm, ])))
  cat("\n")
}
```

A more involved method, that first creates a list of raw Rmarkdown code as a list of character vectors, which are then evaluated in a separate (inline) code chunk with knitr::knit(). This works for all kinds of output, not just base plots.

## Tabbed Example ggplot {.tabset}

```{r}
library(ggplot2)

template <- c(
    "### {{nm}}\n",
    "```{r, echo = FALSE}\n",
    "ggplot(iris[iris$Species == '{{nm}}', ], aes(x = Sepal.Length, y = Sepal.Width)) + geom_point()\n",
    "```\n",
    "\n"
  )

plots <- lapply(
  unique(iris$Species), 
  function(nm) knitr::knit_expand(text = template)
)
```

`r knitr::knit(text = unlist(plots))`
like image 184
Stefan F Avatar answered Oct 20 '22 21:10

Stefan F


As far as I know what you are trying to do is not possible in rmarkdown (but I'd love to stand corrected). But of course we can implement a function to do just that.

I based my answer on this answer by @KRohde, so all the credits goes to him. I just adapted it to work in a simpler markdown document.

The answer is mostly build with JS rather than R, but as the markdown is mostly an HTML I feel JS is a better tool.

Here is the code:

---
output: html_document
---


```{r echo=FALSE, results='asis'}
library(shiny)
addToTabset <- function(title, tabsetId, Panel) {

  tags$script(HTML(paste0("
                   /* Getting the right tabsetPanel */
                   var tabsetTarget = document.getElementById('", tabsetId, "');

                   /* Creating 6-digit tab ID and check, whether it was already assigned. */
                   hrefCode = Math.floor(Math.random()*100000);

                   /* Creating node in the navigation bar */
                   var navNode = document.createElement('li');
                   var linkNode = document.createElement('a');

                   linkNode.appendChild(document.createTextNode('", title, "'));
                   linkNode.setAttribute('data-toggle', 'tab');
                   linkNode.setAttribute('data-value', '", title, "');
                   linkNode.setAttribute('href', '#tab-' + hrefCode);

                   navNode.appendChild(linkNode);
                   tabsetTarget.appendChild(navNode);
                   setTimeout(function(){
                     var tabContent = document.createElement('div');
                     var tabContainerTarget = document.getElementsByClassName('tab-content')[0];

                       tabContent.setAttribute('id', 'tab-' + hrefCode);
                       tabContent.setAttribute('class', 'tab-pane')
                       tabContent.innerHTML = '", gsub('\n', '', Panel, fixed = T), "';

                       tabContainerTarget.appendChild(tabContent);
                       }, 100);
                   ")
  ))
}

```

The code above should stay in a 'setup chunk', as it define an R function to call a JS function that mostly just add the right things to the DOM.

It can then be used when needed, passing the tabPanel title, the 'target' tabset and the normal tabPanel function.

```{r results='asis', echo=FALSE}

shiny::tabsetPanel(id = 'tbSet1',
                   shiny::tabPanel('Tab 1', 'foo'),
                   shiny::tabPanel('Tab 2', 'bar')
)
```



```{r results='asis', echo=FALSE}

addToTabset(title = 'Tab 3',
            tabsetId = 'tbSet1',
            tabPanel(
              h1('This is a title'),
              actionButton('btn',label = 'Clicky button'),
              radioButtons('asd', LETTERS[1:5], LETTERS[1:5])) )

```
like image 41
GGamba Avatar answered Oct 20 '22 20:10

GGamba