Custom Questionnaires / Pages

When using the interactive shiny app you can use one of the different available questionnaires to e.g. conduct a web survey (questionnaire_web_survey()) or collect data from interviewer-administered (phone) interviews (questionnaire_interviewer_administered()).

To be as flexible as possible, you can also modify these existing questionnaires or just build your own one from scratch.

Pages 📃

All questionnaires are built on top of pages. New pages can be created using the function new_page(), the documentation of which is essential reading for those who want to design their own pages.

There exist some helper functions that make it simple to create specific types of question pages:

  • page_choose_one_option()
  • page_freetext()

In general each page has a unique page_id to identify itself and follows a general principle of lifecycle functions. Most of these lifecycle functions are optional, except for the render function, which should return the shiny HTML tags or inputs / outputs to be displayed. All lifecycle functions take at least the shiny session as arguments, other arguments depend on the function itself (supported arguments are listed in new_page()).

Tip: When implementing your own pages, we recommend to use ... as the last parameter in all of them to allow for potentially new parameters to be added.

To find some useful pre-built pages or helper functions to easily generate commonly used pages check out all functions whose name starts with page_ in the reference.

Page Lifecycle

The lifecycle of a page is as follows: First, its condition will be run to check whether the page should be displayed at all. If this function returns FALSE the page will be skipped and the condition for the following one will be evaluated.

If the condition is TRUE, the page will be shown. To do this the function run_before will be run to prepare everything for the page, followed by render to generate the page’s actual outputs (ata returned from run_before is available in render under the name run_before_output).

When the user navigates away from a page and to the next page, the function run_after will be called which can be used to capture user input and save data. Then, the next page’s condition will be checked and so on.

If the user wants to go back to the previous page, run_after will not be called and the previous page will be shown. The condition of the page that the user visited before will not be checked again.

A more detailed description of the page lifecycle can be found in the documentation for new_page().

Saving Data

All existing pages (e.g. page_first_freetext(), page_select_suggestion()) and page helpers (e.g.page_choose_one_option(), page_freetext()) will automatically save user input (as long as it is not explicitly disabled in the app_settings).

When building your own pages from scratch, however, you will have to make sure to specify which data should be saved in the app. This can be done by using set_item_data(). If data is saved using this function, it will automatically be saved with the other data, yet we always recommend verifying that questionnaire data is saved as expected before starting proper data collection.

For a detailed description of what data is saved by default and in which format, examine the section “data” in vignette("app").

Questionnaires 📑

Building your Own ✨

Once you’re comfortable building your own pages, building your own questionnaire is trivial, as a questionnaire is merely a list of pages.

An example for a simple questionnaire would be:

library(occupationMeasurement)
library(shiny)

my_simple_questionnaire <- list(
  new_page(
    page_id = "test",
    render = function(...) {
      return(list(
        p("Hello!"),
        button_previous(),
        button_next()
      ))
    }
  ),

  # Create a custom page to choose your favorite meal
  page_choose_one_option(
    page_id = "favourite_meal",
    question_text = "Please pick your favorite kind of meal.",
    list_of_choices = list(
      "Breakfast" = 1,
      "Lunch" = 2,
      "Dinner" = 3
    )
  )
)

# Test the questionnaire in the app
# app(questionnaire = my_simple_questionnaire)

As the general goal of this package is the measurement of occupations, you will usually want to at least include the core set of pages:

list(
  # ... some of your pages before ...
  page_first_freetext(),
  page_second_freetext(),
  page_select_suggestion(),
  page_none_selected_freetext(),
  page_followup(1),
  page_followup(2)
  # ... some of your pages after ...
)

Modifying an Existing One

Apart from creating a new questionnaire from scratch, it’s also possible to take an existing questionnaire and modify it to suit your needs.

To do this you can modify the questionnaire itself, by adding, removing or overwriting pages into it (as the questionnaire is just a list, it can be modified just like one).

library(occupationMeasurement)
library(shiny)

questionnaire <- questionnaire_web_survey()

# Remove the first page (welcome)
questionnaire[[1]] <- NULL

# Use c() to add a new alternative page at the start
questionnaire <- c(
  list(
    new_page(
      "my_welcome",
      render = function() {
        return(h1("Hello!"))
      }
    )
  ),
  questionnaire
)

# Test the questionnaire in the app
# app(questionnaire = questionnaire)

Alternatively it’s also possible to modify pages themselves (albeit a bit more complicated). Under the hood, pages are essentially also powered by lists and therefore their parameters can just be overwritten e.g. removing a page’s condition check can be done by overwriting it via page$condition <- NULL.

As a page’s different parameters usually depend on each other it’s much easier to accidentaly break something. Therefore it is in general a good practice to overwrite a page’s function by just wrapping it, like the example below.

library(occupationMeasurement)
library(shiny)

questionnaire <- questionnaire_web_survey()

# Add some text to page_select_suggestion
print(questionnaire[[4]]$page_id)
#> [1] "none_selected_freetext"

original_select_suggestion_render <- questionnaire[[4]]$render
questionnaire[[4]]$render <- function(...) {
  return(c(
    list(
      p("~ My custom text before ~")
    ),
    original_select_suggestion_render(...)
  ))
}

# Test the questionnaire in the app
# app(questionnaire = questionnaire)