Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a questionnaire with R Shiny

Tags:

r

shiny

I am trying to learn how to create a questionnaire with Shiny. I need that each question is on a new page. For example, when the user answers to a question, press "Next" button and a new page loads with another question. Any idea on how this is done? Because I want to simplify my code, I created a module for each question. The ui would look like this:

library(shiny)

fluidPage(
  div(class = 'container',
      div(class = 'col-sm-2'),
      div(class = 'col-sm-8',
          h1("Welcome!"),
          p("Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
          br(),
          actionButton("page1", "Start")
          )),
  source("questions/question1.R", local = TRUE)$value,
  source("questions/question2.R", local = TRUE)$value   
)

Module Question 1:

div(class = 'container',
    div(class = 'col-sm-2'),
    div(class = 'col-sm-8',
        radioButtons("question1", "Please select a number: ", choices = c(10,20,30)),
        actionButton("page3", "Next"),
        br()
    )
)

Module Question 2:

div(class = 'container',
    div(class = 'col-sm-2'),
    div(class = 'col-sm-8',
        radioButtons("question2", "Please select a color: ", choices = c("Blue", "Orange", "Red")),
        actionButton("page3", "Next"),
        br()
    )
)

... and the server.R:

server <- function(input, output, session) {

}

So, when the user press "Start" should go to page 1 and so on... Thanks!

like image 605
Bruno Guarita Avatar asked Jul 15 '19 16:07

Bruno Guarita


1 Answers

I think there are a few ways to do this in shiny. I'll start with the simplest which does not solve the problem exactly and add an alternative.

I setup the question .R files as per below:

  • My file path in the code is for a windows os, change as required.

Module Question 1:

div(class = 'container',
    div(class = 'col-sm-2'),
    div(class = 'col-sm-8',
        radioButtons("question1", "Please select a number: ", choices = c(10,20,30)),
        actionButton("block_two", "Next"),
        br()
    )
)

Module Question 2:

div(class = 'container',
    div(class = 'col-sm-2'),
    div(class = 'col-sm-8',
        radioButtons("question2", "Please select a color: ", choices = c("Blue", "Orange", "Red")),
        actionButton("block_three", "Next"),
        br()
    )
)

Simple solution

You could use the observeEvent and renderUI in shiny. This will allow you to pull in the neat code blocks from separate .R files and render them sequentially as the user clicks next.

Note: This however does not render the UI elements on a new page.

library(shiny)

ui <-  fluidPage(
  uiOutput("home"),
  uiOutput("block_one"),
  uiOutput("block_two")
)

server <- function(input, output, session) {

  output$home <- renderUI({
    div(class = 'container', id = "home",
        div(class = 'col-sm-2'),
        div(class = 'col-sm-8',
            h1("Welcome!"),
            p("Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
            br(),
            actionButton("block_one", "Start")
        ))
  })

   observeEvent(input$block_one, {
     output$block_one <- renderUI({ source("questions\\question1.R", local = TRUE)$value })
   })

  observeEvent(input$block_two, {
    output$block_two <- renderUI({ source("questions\\question2.R", local = TRUE)$value })
  })

}

shinyApp(ui, server)

Intricate Solution

This requires you to create a render_page function which one can then use to render these new UI components on a new page. You then just need to create a function for each component and call renderUI.

I am not a massive fan of this as you will then need to create navigation buttons, and then might as well use shinydashboard.

However, if you are planning on creating a really long questionnaire then one could do the below:

I've left the function(...) as is in case you would like to pass additional arguments when rendering the UI components.

library(shiny)

ui <- (htmlOutput("page"))

home <- function(...) {
  args <- list(...)
      div(class = 'container', id = "home",
          div(class = 'col-sm-2'),
          div(class = 'col-sm-8',
              h1("Welcome!"),
              p("Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
              br(),
              actionButton("block_one", "Start")
          ))

}

question_one <- function(...) {
  renderUI({ source("questions\\question1.R", local = TRUE)$value })
}

question_two <- function(...) {
  renderUI({ source("questions\\question2.R", local = TRUE)$value })
}

render_page <- function(...,f, title = "test_app") {
  page <- f(...)
  renderUI({
    fluidPage(page, title = title)
  })
}

server <- function(input, output, session) {

  ## render default page
  output$page <- render_page(f = home)

     observeEvent(input$block_one, {
       output$page <- render_page(f = question_one)
     })

     observeEvent(input$block_two, {
       output$page <- render_page(f = question_two)
     })
}

shinyApp(ui, server)

There is a decent r-blogger post on creating this architecture: https://www.r-bloggers.com/some-thoughts-on-shiny-open-source-render-multiple-pages/

Hope this helps.

like image 54
RK1 Avatar answered Nov 15 '22 03:11

RK1