sd_question(
type = "text",
id = "name",
label = "What is your name?"
)
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 respondent values
Basic example
A simply example of a reactive value is displaying the respondent’s name in a greeting message. First, you would ask the respondent’s name in a question:
Then you can use the sd_output()
function to display the value of the "name"
question in other parts of your survey. For example:
`r sd_output("name", type = "value")`! Welcome,
If the respondent entered “Dave” in the name
question, this would render as:
Welcome, Dave!
Every question has an id
, and the values chosen are stored automatically as id_value
(e.g. if the question id
was name
, then the value would be stored as name_value
). In the sd_output()
function, we append this _value
to the id
to get the object we want to display if the user defines type = "value"
, then we use an appropriate shiny
output function to display it, e.g. shiny::textOutput()
. You can also use sd_output()
to show reactive questions with type = "question"
or other stored values from the server code chunk.
Showing 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 code chunk 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 to display both the original and copied values. For example:
`r sd_output("name", type = "value")`!
Welcome,
if we call you `r sd_output("name_copy", type = "value")`? Is it alright
If the respondent entered “Dave” in the name
question, this would render as:
Welcome, Dave!
Is it alright if we call you Dave?
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 somewhat a limitation of Shiny that we have to live with 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 code chunk “live”, meaning that the labels will be generated when the survey is first loaded. For example, here 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
<- c('option 1', 'option 2', 'option 3')
q1_options
# Randomly sample 3 labels from 1 to 100
<- sample(seq(100), 3)
q1_labels
# Assign the labels to the options
names(q1_options) <- q1_labels
q1_options
#> 49 65 25
#> "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 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 code chunk, the question will be created as a reactive question that can be displayed in the survey 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: 49, 65, 25.
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:
<- data.frame(
design id = rep(1:10, each = 3),
numbers = unlist(lapply(1:10, function(x) sample(seq(100), 3)))
)
head(design)
#> id numbers
#> 1 1 74
#> 2 1 18
#> 3 1 49
#> 4 2 47
#> 5 2 24
#> 6 2 71
Now we can write this design to a csv file:
::write_csv(design, "design.csv") readr
Then in your server code chunk, 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
<- readr::read_csv("design.csv")
design
# Randomly choose a row id
<- sample(design$id, 1)
q1_id
# 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
<- design |>
numbers filter(id == q1_id) |>
pull(numbers)
# Create the options
<- c('option 1', 'option 2', 'option 3')
q1_options 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
)