渲染函数的反应参数

Reactive argument to render functions

我在 flexdashboard 中有一个 table,它的列数可以改变。我可以即时计算列的对齐方式(默认对齐方式将 .45 视为字符向量,因此左对齐值,尽管它是一个数字并且应该右对齐)。问题是我无法将此对齐作为 align 的值传回 renderTable,因为它是一个反应值。

如何将反应式对齐传递回 renderTable 函数的 align 参数? (或者让我用反应式对齐渲染 table 的替代方案)

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)
```

结果:

 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.)

尝试将 aln() 包装在 renderText(...) 中,即

renderTable({
    x <- CO2[1:5, seq_len(nc()), drop = FALSE]
    x[, 1] <- as.character(x[, 1])
    x[3, 1] <- '<b>Mc1</b>'
    x
}, align = renderText(aln()), 
   sanitize.text.function = function(x) x)

首先,发生的具体错误是正确的。函数 renderTable 只能在其第一个参数 renderTable({ ... }, ...) 中包含反应式表达式。所有其他事物都必须是普通对象。因此,在常规环境中放置一个反应值 aln() 实际上就是在其中放置一个 reactive。或者,从 reactive 本身的角度来看,它是在 没有活动的反应上下文 的情况下评估的,就像它在错误消息中所说的那样。

一种使这些东西也具有反应性的解决方法是 renderTable 的包装器,即 renderUI。在 renderUI 中,我们可以反应性地塑造包括对齐在内的整个 table 渲染。 (解决方案部分改编自 here。)

这是您必须将 renderTable 替换为:

renderUI({
  output$table <- renderTable({
      x <- CO2[1:5, seq_len(isolate(nc())), drop = FALSE]
      x[, 1] <- as.character(x[, 1])
      x[3, 1] <- '<b>Mc1</b>'
      x
  }, align = aln(), sanitize.text.function = function(x) x)

  tableOutput("table")
})

快速解释: 使用 output 变量就像您在常规闪亮环境中那样做,如果不是在 markdown 中的话。 renderUI 将呈现的是对 renderTable 的引用,我们每次都会从头开始重新创建它。

注:我在renderTable里面用了一个isolate。原因如下:renderTable 是被动的,并且会在 nc() 发生变化时更新。但这可能发生在周围的 renderUI 对变化做出反应之前。您会看到,在此中间步骤中,renderTable 尝试绘制具有不同列数的 data.frame,但 align 仍然是之前的那个。抛出错误,因为它们不匹配。隔离内部 nc() 会阻止内部 renderTable 更新。但这没关系,因为一旦轮到更新,外部 renderUI 无论如何都会这样做。