Reactivity

Because surveydown renders to a Shiny app, it can take advantage of Shiny’s reactivity features. This means you can create reactive expressions and reactive values that update the survey in response to user input or other events.

This page demonstrates some common use cases for reactive programming in surveydown.

Displaying question values

A simple example of using a reactive value is displaying a question value somewhere else in the survey.

For example, you might ask the respondent’s name in a question and then display their name somewhere else in the survey, like in a greeting message. First, you would ask the respondent’s name in a question in your survey.qmd file:

sd_question(
  type  = "text",
  id    = "name",
  label = "What is your name?"
)

Then you can use the sd_output() function to display the value of the "name" question elsewhere in the survey. For example, you could display the name in a greeting message:

Welcome, `r sd_output("name", type = "value")`!

If the respondent entered “Dave” in the name question, this would render as:

Welcome, Dave!

The type = "value" argument tells sd_output() to display the value of the question rather than the question itself.

How this works under the hood

Every question has it’s own id. To access the value that a respondent chose, we can’t use the same id. To address this, whenever you create a question we automatically create an object stored as id_value to store the value chosen by the respondent.

For example, if the question id was "name", then the value would be stored as "name_value". In the sd_output() function, when you use type = "value", the function automatically appends this _value to the id to get the question value we want to display, then uses an appropriate shiny output function to display it, e.g. shiny::textOutput().

You can also use type = "question" to display questions that are defined in the server() function in your app.R file (for example, see the randomizing question labels section below).

Displaying stored values (e.g. a completion code)

You can use sd_output() with type = "value" to display values that you stored in the server() function.

For example, you might want to display a completion code at the end of the survey. You can do this by defining a completion code in the server() function and then storing it in the survey data using the sd_store_value() function.

Since completion codes are often needed, we made a simple sd_completion_code() function that returns a random numeric completion code as a string. Here’s an example of how to use it:

# Make a 10-digit random number completion code
completion_code <- sd_completion_code(10)

# Store the completion code in the survey data
sd_store_value(completion_code)

This will store the completion code in the survey data under the completion_code column.

You can then display the completion code at the end of the survey using the sd_output() function in your survey.qmd file, like this:

```{r}
Your code is: `r sd_output("completion_code", type = 'value')`
```

This should render as something like this:

Your code is: 7408931907

Displaying the same value in multiple places

The sd_output() function can only be used once per each unique question id because the id gets used in the rendered HTML divs, and HTML with more than one element with the same id is invalid HTML. This is a general issue for Shiny - outputs can only be used once per each unique id (see this GitHub issue on the topic).

The solution that we use is to simply make a copy of the value and then display the copy (this is also the solution on the GitHub issue linked above).

To do so, in the server function in the app.R file use the sd_copy_value() function to create a copy of the value, like this:

sd_copy_value(id = "name", id_copy = "name_copy")

You can then use the sd_output() function in your survey.qmd file to display both the original and copied values. For example:

Welcome, `r sd_output("name", type = "value")`!

Is it alright if we call you `r sd_output("name_copy", type = "value")`?

If the respondent entered “Dave” in the name question, this would render as:

Welcome, Dave!

Is it alright if we call you Dave?

Note

If you find this annoying, we agree! This is a bit of a hack and we are working on a better solution, but it is a limitation of Shiny that we have to live with, at least for now.

Randomizing question labels

Showing a question with randomized labels is a common use case for reactive programming. There are at least two ways to achieve this.

Live randomization

One approach is to generate the random labels in the server function “live”, meaning that the labels will be generated when the survey is first loaded. For example, let’s say in my server function I generate three random numbers between 1 and 100 and use them to create the option labels for a multiple choice question:

# Create a vector of options
q1_options <- c('option 1', 'option 2', 'option 3')

# Randomly sample 3 labels from 1 to 100
q1_labels <- sample(seq(100), 3) 

# Assign the labels to the options
names(q1_options) <- q1_labels 

q1_options
#>        100         89         37 
#> "option 1" "option 2" "option 3"

Remember that the names of the vector are the labels, so the respondents will see the numbers in the names of the above vector, and the values ('option1', etc.) will be stored in the resulting survey data.

To then use these options in a multiple choice question, you would use the sd_question() function also in the server function (not in the survey.qmd file) like this:

sd_question(
  id = "q1",
  type = "mc",  
  label = "Which of these numbers is the largest?",
  option = q1_options
)

By defining this question inside the server function, the question will be created as a reactive question that can then be displayed in the survey.qmd file using the sd_output() function, like this:

```{r}
sd_output(id = "q1", type = "question")
```

One important caveat to this approach is that the random numbers that are generated are not automatically stored in the survey data (because they are generated live). If you needed to store the random numbers, you can use the sd_store_value() function, like this:

sd_store_value(q1_labels, id = "q1_labels")

This would store the numbers in the q1_labels column of the survey data, which would be concatenated into the single string: 100, 89, 37.

You could alternatively use the sd_store_value() function to store each of the random numbers in separate columns, like this:

sd_store_value(q1_labels[1], id = "q1_label_1")
sd_store_value(q1_labels[2], id = "q1_label_2")
sd_store_value(q1_labels[3], id = "q1_label_3")

Pre-defined randomization

An alternative approach is to pre-define the randomized labels and store them in a separate design file, and then randomly select a set of labels for each respondent from the design file.

For example, let’s generate 10 sets of 3 random numbers between 1 and 100 and store them in a data frame:

design <- data.frame(
  id = rep(1:10, each = 3),
  numbers = unlist(lapply(1:10, function(x) sample(seq(100), 3)))
)

head(design)
#>   id numbers
#> 1  1      20
#> 2  1      26
#> 3  1       3
#> 4  2      41
#> 5  2      89
#> 6  2      27

Now we can write this design to a csv file:

readr::write_csv(design, "design.csv")

Then in your server function in the app.R file, you could read in the design file and use it to randomize the labels for each question. Here the only thing you would need to keep track of the randomly chosen row id:

# Read in the design file
design <- readr::read_csv("design.csv")

# Randomly choose a row id
q1_id <- sample(design$id, 1)

# Store the chosen row id in the survey data (here q1_id will be the column name)
sd_store_value(q1_id) 

# Filter the design to get the chosen row
numbers <- design |> 
  filter(id == q1_id) |> 
  pull(numbers)

# Create the options
q1_options <- c('option 1', 'option 2', 'option 3')
names(q1_options) <- numbers

# Create the reactive question
sd_question(
  id = "q1",
  type = "mc",  
  label = "Which of these numbers is the largest?",
  option = q1_options
)

Once again, you would be able to then display this question in the survey.qmd file using the sd_output() function:

```{r}
sd_output(id = "q1", type = "question")
```
Back to top