I have a table in a flexdashboard whose number of columns can change. I can compute the alignment of the columns on the fly (default alignment treats $23.45
as a character vector and thus left aligns the value though it is a number and should be right aligned). The problem is I can't pass this alignment back to renderTable
as a value to align
because it's a reactive value.
How can I pass the reactive alignment back to the renderTable
function's align
argument? (or an alternative that let's me render a table with a reactive alignment)
MWE
---
title: "test"
output: flexdashboard::flex_dashboard
runtime: shiny
---
```{r}
library(flexdashboard)
library(shiny)
```
Inputs {.sidebar}
-------------------------------------
```{r}
selectInput(
"ncols",
label = "How many columns?",
choices = 1:5,
selected = 5
)
```
Column
-------------------------------------
### Test
```{r}
nc <- reactive({input$ncols})
aln <- reactive({substring('lllrr', 1, nc())})
renderTable({
x <- CO2[1:5, seq_len(nc()), drop = FALSE]
x[, 1] <- as.character(x[, 1])
x[3, 1] <- '<b>Mc1</b>'
x
}, align = aln(), sanitize.text.function = function(x) x)
```
Results in:
Warning: Error in .getReactiveEnvironment()$currentContext: Operation not
allowed without an active reactive context. (You tried to do something
that can only be done from inside a reactive expression or observer.)
Try wrapping
aln()
inrenderText(...)
i.e.First things first, the specific error that occurred is quite correct. The function
renderTable
can only hold reactive expressions in its first argumentrenderTable({ ... }, ...)
. All other things must be plain objects. So, putting a reactive valuealn()
there really was putting areactive
in a regular environment. Or, seen from the point of view of thereactive
itself, it was evaluated without an active reactive context like it said in the error message.A workaround to make those things reactive as well, is a wrapper around the
renderTable
, namelyrenderUI
. Inside therenderUI
we can reactively shape the whole table-rendering which includes the align. (Solution partly adapted from here.)Here's what you have to replace the
renderTable
for:Quick explanation: Using the
output
variable is like you would do it in a regular shiny environment, if not in markdown. What will be rendered byrenderUI
is then a reference to therenderTable
which we will re-create from scratch every time.Note: I used an
isolate
inside therenderTable
. Here is why:renderTable
is reactive and would update ifnc()
changes. But that can happen before the surroundingrenderUI
reacts to the change. You see, in this intermediate step,renderTable
tries to plot adata.frame
with a different column count, but thealign
is still the one from before. An error is thrown, because they don't match. Isolating the innernc()
stops the innerrenderTable
from updating. But that's okay, because the outerrenderUI
does this anyways once it is it's turn to update.