--- title: "shinyjs example app walk-through" author: "Dean Attali" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{shinyjs example app walk-through} %\VignetteEngine{knitr::rmarkdown} %\usepackage[utf8]{inputenc} --- ```{r setup, echo = FALSE, message = FALSE} knitr::opts_chunk$set(tidy = FALSE, comment = "#>") ``` # shinyjs example app walk-through This document provides a step-by-step guide on how to add a variety of shinyjs features to a simple app in order to make it more user friendly. *You can view the final Shiny app developed in this simple example [here](https://daattali.com/shiny/shinyjs-basic/).* Suppose we want to have a simple Shiny app that collects a user's basic information (name, age, company) and submits it, along with the time of submission. Here is a very simple implementation of such an app (nothing actually happens when the user "submits"). ``` library(shiny) shinyApp( ui = fluidPage( div(id = "myapp", h2("shinyjs demo"), textInput("name", "Name", ""), numericInput("age", "Age", 30), textInput("company", "Company", ""), p("Timestamp: ", span(date())), actionButton("submit", "Submit") ) ), server = function(input, output) { } ) ``` *Note that I generally don't like running Shiny apps like this and prefer to declare the UI and server separately, but this style is used here for brevity.* Here is what that app would look like ![Demo app](../inst/img/demo-basic-v1.png) ### Add shinyjs features Now suppose we want to add a few features to the app to make it a bit more user-friendly. First we need to set up the app to use `shinyjs` by making a call to `useShinyjs()` in the Shiny app's UI. Here are 7 features we'll add to the app, each followed with the code to implement it using `shinyjs`: **1. The "Name" field is mandatory and thus the "Submit" button should not be enabled if there is no name** In the server portion, add the following code ``` observe({ if (is.null(input$name) || input$name == "") { shinyjs::disable("submit") } else { shinyjs::enable("submit") } }) ``` Or instead you can use the `toggleState` function and pass it a `condition`: ``` observe({ shinyjs::toggleState("submit", !is.null(input$name) && input$name != "") }) ``` You can use the optional `condition` in some other functions as well, which can be very useful to make your code shorter and more understandable. **2. The "Age" and "Company" fields are optional and we want to have the ability to hide that section of the form** First, we need to section off the "Age" and "Company" elements into their own section, so we surround them with a `div` ``` div(id = "advanced", numericInput("age", "Age", 30), textInput("company", "Company", "") ) ``` We also need to add a link in the UI that will be used to hide/show the section ``` a(id = "toggleAdvanced", "Show/hide advanced info") ``` Lastly, we need to tell Shiny to show/hide the section when the link is clicked by adding this code to the server ``` shinyjs::onclick("toggleAdvanced", shinyjs::toggle(id = "advanced", anim = TRUE)) ``` **3. Similarly, since we don't really care about "Age" and "Company" too much, we want to hide them initially when the form loads** Simply surround the section we want to hide initially with `shinyjs::hidden` ``` shinyjs::hidden( div(id = "advanced", ... )) ``` **4. The user should be able to update the "Timestamp" in case he spends way too long filling out the form (not very realistic here, and the timestamp should ideally be determined when the button is clicked, but it's good enough for illustration purposes)** First, we need to add an "Update" link to click on, and we need to give the element showing the time an `id` so that we can refer to it later when we want to change its contents. To do that, replace `p("Timestamp: ", span(date()))` with ``` p("Timestamp: ", span(id = "time", date()), a(id = "update", "Update")) ``` Now we need to tell Shiny what to do when "Update" is clicked by adding this to the server ``` shinyjs::onclick("update", shinyjs::html("time", date())) ``` **5. Some users may find it hard to read the small text in the app, so there should be an option to increase the font size** First, we need to add checkbox to the UI ``` checkboxInput("big", "Bigger text", FALSE) ``` In order to make the text bigger, we will use CSS. So let's add an appropriate CSS rule by adding this code to the UI ``` shinyjs::inlineCSS(list(.big = "font-size: 2em")) ``` Lastly, we want the text to be big or small depending on whether the checkbox is checked by adding this code to the server ``` observe({ if (input$big) { shinyjs::addClass("myapp", "big") } else { shinyjs::removeClass("myapp", "big") } }) ``` Or, again, we can use the `toggleClass` function with the `condition` argument: ``` observe({ shinyjs::toggleClass("myapp", "big", input$big) }) ``` **6. Give the user a "Thank you" message upon submission** Simply add the following to the server ``` observeEvent(input$submit, { shinyjs::alert("Thank you!") }) ``` **7. Allow the user to reset the form** First let's add a button to the UI ``` actionButton("reset", "Reset form") ``` And when the button is clicked, reset the form ``` observeEvent(input$reset, { shinyjs::reset("myapp") }) ``` ### Final code The final code looks like this ``` library(shiny) shinyApp( ui = fluidPage( shinyjs::useShinyjs(), shinyjs::inlineCSS(list(.big = "font-size: 2em")), div(id = "myapp", h2("shinyjs demo"), checkboxInput("big", "Bigger text", FALSE), textInput("name", "Name", ""), a(id = "toggleAdvanced", "Show/hide advanced info", href = "#"), shinyjs::hidden( div(id = "advanced", numericInput("age", "Age", 30), textInput("company", "Company", "") ) ), p("Timestamp: ", span(id = "time", date()), a(id = "update", "Update", href = "#") ), actionButton("submit", "Submit"), actionButton("reset", "Reset form") ) ), server = function(input, output) { observe({ shinyjs::toggleState("submit", !is.null(input$name) && input$name != "") }) shinyjs::onclick("toggleAdvanced", shinyjs::toggle(id = "advanced", anim = TRUE)) shinyjs::onclick("update", shinyjs::html("time", date())) observe({ shinyjs::toggleClass("myapp", "big", input$big) }) observeEvent(input$submit, { shinyjs::alert("Thank you!") }) observeEvent(input$reset, { shinyjs::reset("myapp") }) } ) ``` You can view the final app [here](https://daattali.com/shiny/shinyjs-basic/).