Reactivity with OJS
Client-side interactive documents through integration with OJS
Since quarto-live
interactive code blocks are powered by WebAssembly, rich serverless and interactive experiences can be provided to the reader.
In particular, reactivity can easily be added to Quarto HTML documents through the use of OJS blocks, and quarto-live
code blocks can be integrated as a producer and consumer of reactive variables.
Using OJS variables in code cells
Before we integrate OJS variables into a quarto-live
code block, first let’s create a reactive input using an ojs
code block.
There are many standard inputs available through OJS, here we create a set of checkboxes for the variable islands
.
Source
reactivity.qmd
```{ojs}
//| echo: false
viewof islands = Inputs.checkbox(
["Torgersen", "Biscoe", "Dream"],
{
value: ["Torgersen", "Biscoe"],
label: "Islands:",
}
)
```
Output
We can now use the variable islands
in a quarto-live
interactive code cell by setting the input
code cell option. The option takes a list of OJS variables formatted as YAML.
Source
reactivity.qmd
```{webr}
#| input:
#| - islands
islands
```
Output
Try checking or unchecking boxes above. The code cell output will reactively update to the changes.
Example: Interactive histogram
Source
histogram.qmd
```{webr}
#| input:
#| - n
hist(rnorm(n))
```
```{ojs}
//| echo: false
viewof n = Inputs.range([0, 501], {step: 1, label: "n"})
```
Output
Defining OJS variables from a code block
It’s possible to do the same in reverse, defining an OJS variable from a quarto-live
code block. Use the define
cell option to give a list of variables to be exported for use in ojs
blocks throughout the document.
Source
reactivity.qmd
```{webr}
#| define:
#| - foo
#| - bar
foo <- rnorm(5)
bar <- rnorm(5)
```
```{ojs}
d3.sum(foo) + d3.max(bar)
```
Output
.sum(foo) + d3.max(bar) d3
Notice how re-evaluating the code block reactively updates the value calculated in the OJS block.
You might see an OJS error briefly appear as the page loads. This is normal, caused by the fact that the variables defined by the quarto-live
cell do not exist until after the WebAssembly engine has loaded and executed the contents of the code block.
Interactive documents
The above described OJS integration can be used to implement complex computational and statistical methods using R or Python code, executed under WebAssembly, which can then be invoked reactively with OJS.
This is a useful pattern for creating engaging documents with a client-side rendering approach. See also the Quarto dashboard example.
Invoking exported R and Python functions from OJS is generally more efficient than re-evaluating a quarto-live
code block, since the code does not need to be re-parsed each time it needs to run.
Example: Penguins interactive plot
First, define an OJS function using a quarto-live
code block. You can hide the code cell from the rendered Quarto document output by setting the code cell options edit: false
and output: false
.
Your function should take reactive inputs as arguments, and be exported to OJS by setting the define
code cell option. Here our exported function is named do_penguin_density()
.
dashboard.qmd
```{webr}
#| edit: false
#| output: false
#| define:
#| - do_penguins_density
do_penguins_density <- function(measure, sp) {
filtered <- penguins |> filter(species == sp)
ggplot(data = filtered, aes(x = .data[[measure]])) +
geom_density(aes(fill = species), alpha = 0.8, position = "identity") +
labs(title = "Penguins 🐧")
}
```
Next, create OJS reactive inputs and invoke your exported function, here named do_penguin_density()
, using the reactive inputs as arguments.
dashboard.qmd
```{ojs}
//| echo: false
viewof species = Inputs.checkbox(
[ "Adelie", "Chinstrap", "Gentoo" ],
{ value: ["Adelie", "Chinstrap"], label: "Species" }
);
viewof measure = Inputs.select(
[ "flipper_length_mm", "bill_length_mm", "bill_depth_mm", "body_mass_g" ],
{ label: "Measure" }
);
do_penguins_density(measure, species);
```