Table of contents
You can install interviewer in R using devtools::install_github
:
devtools::install_github('mtrybulec/interviewer')
library(interviewer)
An interviewer
questionnaire is "hosted" in a standard Shiny application - it's just
a single output item in the ui.R
file (plus an additional "admin" call)
and a single function call in the server.R
file.
A minimal example follows (all functions are referenced using package names
just to make it clear what comes from which package):
ui.R:
library(interviewer)
library(shiny)
shiny::fluidPage(
interviewer::useInterviewer(),
shiny::uiOutput(outputId = "questionnaireOutput")
)
The code above uses Shiny's fluidPage
but any other layout function could be used as well.
useInterviewer
must be called at least once to set up interviewer
-specific CSS and JavaScript.
And since interviewer
uses the shinyjs
package, it calls its useShinyjs
method as well.
The second instruction defines the output field where all generated questionnaire output
will be displayed. Since it's just one output field, you can add any other valid Shiny output
before or after interviewer
code.
server.R:
library(interviewer)
library(shiny)
function(input, output) {
output$questionnaireOutput <-
interviewer::questionnaire(
label = "Simple DEMO",
welcome = "Welcome",
interviewer::question.single(
id = "q1",
label = "Question 1",
responses = interviewer::buildResponses(
id = c("a", "b", "c"),
label = c("response A", "response B", "response C")
)
),
goodbye = "Done!",
exit = function(data) {
cat("Done:\n")
print(data)
}
)
}
The questionnaire
call above defines the title of the survey that will be displayed
at the top of the output field. Next, it defines a welcome
page
that will be displayed before the interview starts. Here, this is just text,
but it can be any valid Shiny output.
Next come a series of questions, UI elements, page-breaks, and function definitions
that make up the full questionnaire definition. Here, it's a single, single-choice question
that displays "Question 1"
as its label, and displays three responses (in this case, using radio-buttons).
While the buildResponses
function is called here to construct response ids and labels,
this is just a standard R data.frame. As such, you can construct this data.frame using
whatever R code you'd like to use (including things like randomization of responses).
buildResponses
is just a helper function like some other question and response helper functions.
While the texts "Question 1"
and "response A"
to "response C"
will be displayed on the screen,
the data will be saved in the "q1"
column, and it will be one of the values: "a"
, "b"
, or "c"
.
The penultimate argument defines a goodbye
page that will be displayed after the interview ends.
Here, again, this is just text, but it can be any valid Shiny output.
The final argument of the questionnaire
- exit
- defines a callback function
that will be called when the respondent is done with the interview. The function will be called
with the data.frame that contains the respondent's answers. Here, it just prints the results,
but you may choose to save the data to a database, a file, etc.:
Done:
q1
1 b
Note on the ordering of arguments in the questionnaire
call: this is R code -
a standard call to a function - so, the R syntax rules apply. The order of the named parameters
(label
, welcome
, goodbye
, and exit
) doesn't matter (as long as they're named)
as well as where the question definitions appear. However, the relative ordering
of question definitions obviously matters - they will be displayed
to the respondent in the order defined.
The interviewer
package comes with a runExample()
function -
you can use it to run the different examples discussed below.
Call runExample()
with no arguments to get a list of valid examples.
Pass the example name, as a string parameter, to run a specific example (e.g. runExample("single-choice")
).
Obviously, you can build longer than just one-question questionnaires.
Just add more question definitions in the ...
part of the call to questionnaire
.
You may also choose to break the questionnaire into pages or screens - so that the respondent in not overwhelmed when presented with a very long web page.
From now on ui.R
code will be omitted - it's the same for all examples.
And screenshots of the welcome and goodbye pages will also be dropped.
And, for most examples, we'll define a single response list that can be reused in multiple questions (which, BTW, shows how R is the questionnaire scripting language and can be used to define common question elements).
server.R:
library(interviewer)
library(shiny)
function(input, output) {
responses = interviewer::buildResponses(
id = c("a", "b", "c"),
label = c("response A", "response B", "response C")
)
output$questionnaireOutput <-
interviewer::questionnaire(
label = "Simple DEMO",
welcome = "Welcome",
interviewer::question.single(
id = "q1",
label = "Question 1",
responses = responses
),
interviewer::question.single(
id = "q2",
label = "Question 2",
responses = responses
),
interviewer::pageBreak(),
interviewer::question.single(
id = "q3",
label = "Question 3",
responses = responses
),
goodbye = "Done!",
exit = function(data) {
cat("Done:\n")
print(data)
}
)
}
In the above, we have two questions on the first page, and one question on the second page.
You can use the Back and Next buttons to navigate to earlier questions and then back to the current question.
And the resulting data.frame:
Done:
q1 q2 q3
1 a b c
Up till now, only simple, single-response questions were shown. However, interviewer
comes
equipped with several question templates for the most common scenarios
(plus, you can build your questions from scratch, but that's for later).
All pre-defined question templates take a required
argument; if it's TRUE
(default),
the validation function will not let the respondent move to the next page without answering the question.
If it's FALSE
, the respondent may choose not to select/provide a response.
Single-choice questions have several options: you can display those as radio-buttons (recommended, default) or combo-boxes. And if you decide to use radio-buttons, you can display those vertically (default) or inline/horizontally.
server.R - radio-buttons:
. . .
interviewer::question.single(
id = "RadioButtonsStandard",
label = "Radio-buttons, standard",
responses = responses
),
interviewer::question.single(
id = "RadioButtonsInline",
label = "Radio-buttons, inline (inline set to TRUE)",
responses = responses,
inline = TRUE
),
interviewer::question.single(
id = "RadioButtonsOptional",
label = paste("Radio-buttons, no response required",
"(required set to FALSE; a second click on a selected radio-button",
"deselects it)"),
responses = responses,
required = FALSE
),
interviewer::question.single(
id = "RadioButtonsNarrow",
label = "Radio-buttons, inline and narrow (width set to '250px')",
responses = responses,
inline = TRUE,
width = "250px"
),
. . .
The first question is just a standard single-choice question as seen earlier.
The second question uses inline
(horizontal) layout.
The third question sets required
to FALSE
, meaning the respondent is free to now answer the question.
The final question shows basic styling of the question layout - changing its width
(note that, in this case, radio-buttons wrap over multiple lines).
All questions are initially displayed without any pre-selected responses; the respondent is always free to deselect a response, even when the question uses radio-buttons.
And now, a similar set of questions, but using (single-selection) combo-boxes:
server.R - single-choice combo-boxes:
. . .
interviewer::question.single(
id = "ComboBoxStandard",
label = "Combo-box, standard (use.select set to TRUE)",
responses = responses,
use.select = TRUE
),
interviewer::question.single(
id = "ComboBoxPlaceholder",
label = "Combo-box, custom message (placeholder set to 'I need a response!')",
responses = responses,
use.select = TRUE,
placeholder = "I need a response!"
),
interviewer::question.single(
id = "ComboBoxOptional",
label = "Combo-box, no response required (required set to FALSE)",
responses = responses,
use.select = TRUE,
placeholder = "This question is optional",
required = FALSE
),
interviewer::buildNonQuestion(
ui = shiny::p(paste(
"Note how the combo-box below is displayed on top of the survey buttons.",
"Take care when designing such screens."
))
),
interviewer::question.single(
id = "ComboBoxNarrow",
label = "Combo-box, narrow (width set to '200px')",
responses = responses,
use.select = TRUE,
width = "200px"
),
. . .
The first question is just a standard single-choice question,
but sets use.select
to TRUE
to display as a combo-box.
The second question defines a placeholder
that will be displayed as a hint
in the edit area of the combo-box before any selections are made.
The third question sets required
to FALSE
, meaning the respondent is free to not answer the question.
The final question shows basic styling of the question layout - changing its width
.
A note on using combo-boxes: they're harder to use (for respondents) than radio-buttons - they require two clicks to select a response instead of one. Combo-boxes can also display on top of subsequent questions or navigation buttons (as in the screenshot above), hiding contents and requiring yet another click outside of the pull-down list.
So, combo-boxes should really be used only when the list of responses is very long (respondents can then filter the list by typing a part of the response text).
The "non-question" before the final question is an example of arbitrary Shiny output that can be displayed anywhere in the questionnaire.
The above code comes from the "single-choice"
example - execute runExample("single-choice")
.
Similarly to single-choice questions, multiple-choice questions have several options: you can display those as check-boxes (recommended, default) or combo-boxes. And if you decide to use check-boxes, you can display those vertically (default) or inline/horizontally.
server.R - check-boxes:
. . .
interviewer::question.multiple(
id = "CheckBoxesStandard",
label = "Check-boxes, standard",
responses = responses
),
interviewer::question.multiple(
id = "CheckBoxesInline",
label = "Check-boxes, inline (inline set to TRUE)",
responses = responses,
inline = TRUE
),
interviewer::question.multiple(
id = "CheckBoxesOptional",
label = "Check-boxes, no response required (required set to FALSE)",
responses = responses,
required = FALSE
),
interviewer::question.multiple(
id = "CheckBoxesNarrow",
label = "Check-boxes, inline and narrow (width set to '250px')",
responses = responses,
inline = TRUE,
width = "250px"
),
. . .
The first question is just a standard multiple-choice question.
The second question uses inline
(horizontal) layout.
The third question sets required
to FALSE
, meaning the respondent is free to now answer the question.
The final question shows basic styling of the question layout - changing its width
(note that, in this case, check-boxes wrap over multiple lines).
All questions are initially displayed without any pre-selected responses; the respondent is always free to deselect a response.
And again, a similar set of questions, but using (multiple-selection) combo-boxes:
server.R - multiple-choice combo-boxes:
. . .
interviewer::question.multiple(
id = "ComboBoxStandard",
label = "Combo-box, standard (use.select set to TRUE)",
responses = responses,
use.select = TRUE
),
interviewer::question.multiple(
id = "ComboBoxPlaceholder",
label = "Combo-box, custom message (placeholder set to 'I need a response!')",
responses = responses,
use.select = TRUE,
placeholder = "I need a response!"
),
interviewer::question.multiple(
id = "ComboBoxOptional",
label = "Combo-box, no response required (required set to FALSE)",
responses = responses,
use.select = TRUE,
placeholder = "This question is optional",
required = FALSE
),
interviewer::buildNonQuestion(
ui = shiny::p(paste(
"Note how the combo-box below is displayed on top of the survey buttons.",
"Take care when designing such screens."
))
),
interviewer::question.multiple(
id = "ComboBoxNarrow",
label = "Combo-box, narrow (width set to '200px')",
responses = responses,
use.select = TRUE,
width = "200px"
),
. . .
The first question is just a standard multiple-choice question,
but sets use.select
to TRUE
to display as a combo-box.
The second question defines a placeholder
that will be displayed as a hint
in the edit area of the combo-box before any selections are made.
The third question sets required
to FALSE
, meaning the respondent is free to not answer the question.
The final question shows basic styling of the question layout - changing its width
.
The above code comes from the "multiple-choice"
example - execute runExample("multiple-choice")
.
The resulting data.frame will have multiple responses coded as comma-separted lists of response identifiers, for example:
Done:
CheckBoxesStandard CheckBoxesInline CheckBoxesOptional CheckBoxesNarrow . . .
1 a a,b a,b,c b,c . . .
In progress, but check out interviewer
examples using runExample()
...
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.