Реактивный аргумент для рендеринга функций

У меня есть таблица на гибкой панели инструментов, количество столбцов которой может меняться. Я могу вычислить выравнивание столбцов на лету (выравнивание по умолчанию рассматривает $23.45 как вектор символов и, таким образом, выравнивает значение по левому краю, хотя это число и должно быть выровнено по правому краю). Проблема в том, что я не могу передать это выравнивание обратно в renderTable в качестве значения align, потому что это реактивное значение.

Как я могу передать реактивное выравнивание обратно аргументу align функции renderTable? (или альтернативу, которая позволяет мне визуализировать таблицу с реактивным выравниванием)

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

person Tyler Rinker    schedule 05.01.2018    source источник


Ответы (2)


Попробуйте обернуть 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)
person Weihuang Wong    schedule 05.01.2018
comment
Мне понравилось это решение, так как я знал, что неправильно использую реактивный элемент, но не понимал, что могу просто использовать renderText. Гениальный и универсальный для множества ситуаций. - person Tyler Rinker; 05.01.2018
comment
Я просто хочу добавить - это работает только потому, что renderTable принимает аргументы, которые можно вызывать как функции. Его нельзя распространять на другие функции рендеринга. Это также означает, что ни reactive, ни renderText не нужны, и вы можете передать такие функции, как aln <- function() substring('lllrr', 1, nc()), renderTable(..., align = aln) - person greg L; 05.01.2018

Во-первых, конкретная ошибка, которая произошла, вполне верна. Функция renderTable может содержать только реактивные выражения в своем первом аргументе renderTable({ ... }, ...). Все остальное должно быть простыми объектами. Таким образом, установка реактивного значения aln() на самом деле означала помещение reactive в обычную среду. Или, с точки зрения самого reactive, он был оценен без активного реактивного контекста, как сказано в сообщении об ошибке.

Обходной путь, позволяющий сделать эти вещи реактивными, - это оболочка вокруг renderTable, а именно renderUI. Внутри renderUI мы можем реактивно формировать всю визуализацию таблицы, включая выравнивание. (Решение частично адаптировано из здесь.)

Вот чем нужно заменить 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 аналогично тому, как если бы вы не делали это в обычной блестящей среде, если не в уценке. То, что будет рендерить renderUI, будет тогда ссылкой на renderTable, который мы будем каждый раз воссоздавать с нуля.

Примечание. Я использовал isolate внутри renderTable. Вот почему: renderTable является реактивным и будет обновляться, если nc() изменится. Но это может произойти до того, как окружающие renderUI отреагируют на изменение. Видите ли, на этом промежуточном этапе renderTable пытается построить data.frame с другим количеством столбцов, но align по-прежнему тот, что был раньше. Выдается ошибка, потому что они не совпадают. Изоляция внутреннего nc() останавливает обновление внутреннего renderTable. Но это нормально, потому что внешний renderUI делает это в любом случае, когда наступает очередь обновления.

person K. Rohde    schedule 05.01.2018
comment
Большое спасибо за это решение. Это было очень информативно. +1 - person Tyler Rinker; 05.01.2018