Переключить вывод фрагмента с помощью HTML-тега сведений

Я пишу несколько руководств, используя blogdown. По педагогическим соображениям я хочу, чтобы мои ученики думали, прежде чем увидеть решение. Вот мой текущий код.

Исходный

---
title: "Toggle Chuck Output Using details Tag"
output: html_document
---

```{r calc, prompt=TRUE, eval=FALSE}
90 + 30
```

<details>
  <summary>Toggle output</summary>
```{r, ref.label='calc', echo=FALSE, prompt=TRUE}
```
</details>

Вот моя попытка:

Чтобы избежать повторного написания тегов HTML, я думаю, мне нужно определить функцию, аналогичную...

togglable <- function(label, summary = "Toggle output"){
  cat('<details>')
  cat(' <summary>', summary, '</summary>', sep = '')

  # Code to print output using 'ref.label' should go here.
  # The following doesn't work. 
  knitr::knit_print(knitr:::knit_code$get(label))

  cat('</details>')
}

.... затем замените блок <detals>...</details> фрагментом кода R, подобным следующему:

Сценарий 1 (лучше)

```{r usecase1, echo=FALSE, results='asis'}
togglable(label = "calc")
```

Я пытался заставить его работать, но тщетно.

Еще кое-что. Если возможно, я бы хотел, чтобы эта функция togglable() переопределяла параметры фрагмента, чтобы мне даже не нужно было писать echo=FALSE, results='asis', потому что следующий фрагмент выглядел бы лучше.

Сценарий 2 (наилучший)

```{r usecase2}
togglable(label = "calc")
```

Подводя итог, хочу задать следующие вопросы.

  1. Как мне определить эту функцию togglable(), чтобы она вела себя так же, как исходный блок <detals>...</details>?
  2. Возможно ли, что эта функция переопределяет параметры (в частности, echo и results) для чанка, где эта функция вызывается? Если да, то как?
  3. В качестве альтернативы, есть ли другая идея, как получить результат исходного кода без повторного написания тегов HTML?

Большое спасибо!


person Kenji    schedule 14.09.2018    source источник


Ответы (1)


Это можно сделать с помощью комбинации параметра фрагмента ref.label (для повторного использования фрагментов). , функция фрагмента (для печати тега <details>) и option hook (для изменения параметров чанка при отображении результатов.

---
title: "Toggle Chuck Output Using details Tag"
output: html_document
---

```{r setup, include=FALSE}
library(knitr)

knit_hooks$set(showDetails = function(before, options, envir) {
  if (before) {
    return("<details>\n")
  } else {
    return("\n</details>")
  }
})

opts_hooks$set(showDetails = function(options) {
  if(options$showDetails) {
    options$echo = FALSE
    options$results = "asis"
  }
  return(options)
})
```
  
```{r calc, prompt=TRUE, eval=FALSE}
90 + 30
```


```{r, ref.label="calc", showDetails = TRUE}
```

Как это работает:

  • Хук чанка выполняется до и после каждого чанка, где опция showDetails не NULL. Он печатает (возвращает) соответствующий HTML.
  • Перехватчик параметров настраивает другие параметры (echo и results) для каждого фрагмента, где showDetails равен TRUE.

Код может быть дополнительно улучшен путем глобальной установки параметров фрагмента calc, чтобы вам не приходилось повторять их для всех других фрагментов только кода показа: добавьте opts_chunk$set(prompt = TRUE, eval = FALSE) в фрагмент настройки и options$eval = TRUE в перехватчик параметров.

Кроме того, если вы хотите использовать теги <detail> по умолчанию при использовании ref.label, вы можете использовать ref.label в качестве опции-хука:

```{r setup, include=FALSE}
library(knitr)

opts_chunk$set(prompt = TRUE, eval = FALSE)

knit_hooks$set(showDetails = function(before, options, envir) {
  if (before) {
    return("<details>\n")
  } else {
    return("\n</details>")
  }
})

opts_hooks$set(ref.label = function(options) {
  options$echo = FALSE
  options$results = "asis"
  options$eval = TRUE
  options$showDetails = TRUE
  
  return(options)
})
```
  
```{r calc}
90 + 30
```


```{r, ref.label="calc"}
```
person CL.    schedule 14.09.2018
comment
Я только что понял, что этот ответ предполагает, что отделение вычислений от вывода является фактическим требованием (именно поэтому необходимо повторное использование фрагмента). Но это может быть просто частью проблемы XY в вопросе. В этом случае лучшим решением может быть выходной хук, не требующий повторного использования кусок. Но, с другой стороны, этот подход также может быть полезен, поскольку между кодом и его выводом можно добавить другой текст/фрагменты (например, содержащие пояснения). Пожалуйста, дайте мне знать, если это то, что вам нужно. - person CL.; 14.09.2018
comment
Спасибо за отличный ответ! Я думаю, что хочу поместить некоторый текст между проблемой и ответом, и поэтому этот подход лучше подходит для моих нужд. Тем не менее, у меня есть несколько проблем. (1) Стиль вывода исчез. Может быть, мне следует добавить HTML-код, чтобы имитировать вывод по умолчанию? (что, я думаю, должно быть простым.) (2) Мне интересно, как расширить содержимое блока сведений, потому что за ответом иногда могут следовать некоторые комментарии, которые также должны быть скрыты. Пожалуйста, позвольте мне потратить еще немного времени, чтобы полностью усвоить ваши предложения. - person Kenji; 14.09.2018
comment
Рад, что смог помочь до сих пор. Относительно (1): Это связано с results = "asis". Вы можете удалить options$results = "asis" из хука опций, чтобы получить форматирование по умолчанию. Я думал, это то, что вы просили. (2): Сложно… Вы можете напечатать </details> только при условии, что какой-то параметр будет указан, а затем закрыть тег вручную, например это. - person CL.; 14.09.2018
comment
Я только что понял, что, к сожалению, этот ответ генерирует недопустимый HTML (поскольку pandoc заключает теги details в теги p). Браузеры, кажется, исправляют это молча, но если для вас критерием является допустимый HTML, это может быть проблемой. (Я не знаю никакого обходного пути.) - person CL.; 14.09.2018
comment
О, на самом деле это отличная идея - вручную закрыть тег сведений .... может быть, я мог бы легко включить еще один фрагмент кода в блок сведений, что очень полезно. Что касается второго комментария, не обращайте внимания! (Я заглянул в источник, но сам не смог найти проблему.) Спасибо! - person Kenji; 14.09.2018
comment
Я немного изменил свой вопрос (добавил № 3), надеясь, что ваш ответ выглядит более подходящим (потому что это так). - person Kenji; 14.09.2018