<- fluidPage(
ui
# Page Title
"Night Shift Comprehensive Questionnaire [Part 1]",
# Basic Input Fields
fluidRow(
column(4, textInput("compname", "Company Name: ", "")),
column(4, dateInput("birthyear", "Date of Birth: ")),
column(4, textInput("name", "Name: ", ""))
),
# Main Questionnaire Area
"Please describe any past medical conditions:",
textAreaInput("past_history", "", rows = 3),
# Submit Button
actionButton("save1", "Save")
)
8 Shiny and HTML and javascript
8.1 Why we use HTM and JavaScript into Shiny apps
Integrating HTML and JavaScript into Shiny apps is important for several key reasons:
1. Enhanced Customization
Fine-Grained Control: While Shiny provides a solid set of UI components, HTML gives you absolute control over the structure and arrangement of elements. You can build layouts and components precisely as you envision them.
Unique Styling: CSS allows you to style every aspect of your app’s appearance. Go beyond Shiny’s default styling to establish a distinct visual identity that matches your branding or needs.
Advanced Visuals: Integrate external JavaScript charting libraries (D3.js, Plotly.js, Leaflet, etc.) to create highly interactive and customizable visualizations that go beyond what Shiny offers out of the box.
2. Unlocking Interactivity
Dynamic Behavior: JavaScript empowers you to modify the UI directly in response to user actions. Create elements that appear/disappear, animations, and seamless user experiences without full page refreshes. Complex Input Handling: Use JavaScript for custom input validation that goes beyond Shiny’s basic checks (e.g., real-time field validation, complex form logic).
External Library Integration: Tap into the vast world of JavaScript libraries for specialized UI widgets, interactive elements, and functionality that extends Shiny’s capabilities.
3. Bridging the Gap
Leveraging Web Development Skills: If you or your team have HTML, CSS, and JavaScript expertise, you can directly apply these skills within your Shiny apps.
Reusing Existing Components: Integrate pre-existing HTML templates or JavaScript components you might already have, saving development time. Example Scenarios
Custom Dashboard: Build a highly tailored dashboard interface with a specific grid layout, custom styling, and interactive visualizations using a charting library.
Responsive Layouts: Design Shiny apps that adapt seamlessly to different screen sizes, a feature that can be more easily achieved using HTML/CSS techniques.
Interactive Map: Embed a map from Leaflet allowing users to zoom, pan, and interact with data markers in ways not directly supported by standard Shiny components.
4. Let’s be mindful:
Trade-off: Too much reliance on raw HTML/JS can increase code complexity and potentially add to maintenance overhead. It’s best to use it strategically where Shiny’s built-in features don’t fully meet your needs.
ShinyJS: The shinyjs package in R provides useful functions to make standard JavaScript interactions easier within the Shiny framework.
8.3 Using JavaScript in Shiny
Here are ui, server, global files and app.js
<- fluidPage(
ui $head(
tags$link(href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap", rel="stylesheet"),
tags$link(rel="stylesheet", type="text/css", href="styles.css"),
tags$script(src="app.js")
tags
),# Page Title
h3("Night Shift Comprehensive Questionnaire [Part 1]", class="h3-center"),
div(class="table-like",
# Basic Input Fields
fluidRow(
column(4, textInput("compname", "Company Name: ", "")),
column(4, dateInput("birthyear", "Date of Birth: ")),
column(4, textInput("name", "Name: ", ""))
)),# Main Questionnaire Area
HTML("Please describe any past medical conditions:"),
textAreaInput("past_history", "", rows = 3),
hr(),
h4(">> Sleep Disorders (Insomnia Index)"),
h5("1~3. Please indicate how severe the problems in each of the following items have been in the past 2 weeks.", style="font-weight: bold;margin-left:5px;"),
::dataTableOutput('input_table_sleep_1'),
DTbr(),
# Submit Button
actionButton("save1", "Save")
)
for UI,
- Placeholder: The line
DT::dataTableOutput('input_table_sleep_1')
acts as a placeholder within your user interface. It signals to Shiny, “Reserve a space here – I’ll dynamically populate it with a data table later.” - Output ID: The ‘input_table_sleep_1’ is a crucial identifier. It allows the Shiny server to target this specific element to insert the generated table content
= function(input, output, session){
server$input_table_sleep_1 <- DT::renderDataTable(
outputescape = FALSE, selection = 'none', server = FALSE,
q1m, options = list(dom = 't', paging = FALSE, ordering = FALSE),
class = 'cell-border stripe'
)
}
for server
Let’s break down what happens within this renderDataTable section:
- Data Source: You provide q1m as the data that will make up your table’s content. Remember, q1m is the HTML matrix generated by your generate_html function, containing the structure of survey questions and the radio button inputs.
- escape = FALSE: Tells Shiny to render the HTML from q1m directly, so your radio button survey inputs appear correctly.
- selection, paging, ordering: These control table features. You’ve disabled selection, pagination, and ordering for simplicity.
- class: Applies CSS classes for visual styling of your table.
Key Function: The DT::renderDataTable()
is the workhorse that takes the HTML structure, combines it with styling options, and transforms it into a proper interactive DataTable to be displayed within the UI placeholder.
library(shinyalert)
library(shiny)
library(DT)
library(shinyWidgets)
library(tidyverse)
# Question codes
<- c("slp_q1", "slp_q2", "slp_q3")
q1_code <- c("1. Difficulty falling asleep", "2. Difficulty staying asleep", "3. Waking up too early")
q1_source
# Answer option codes
<- c("opt1_1", "opt1_2", "opt1_3", "opt1_4", "opt1_5")
opt1_code <- c("None", "Slightly", "Moderately", "Severely", "Very<br>Severely")
opt1_source <- c(0, 1, 2, 3, 4)
opt1_value
<- function(q_code, q_source, opt_value, opt_source) {
generate_html <- sapply(q_code, function(code) {
html_matrix sapply(opt_value, function(value) {
sprintf('<input type="radio" class="my-radio-button" id="%s-%d" name="%s" value="%d"/>',
code, value, code, value)USE.NAMES = FALSE)
}, USE.NAMES = FALSE)
}, <- matrix(unlist(html_matrix), nrow = length(q_code), byrow = TRUE)
html_matrix rownames(html_matrix) <- q_source
colnames(html_matrix) <- opt_source
return(html_matrix)
}
# Use the function to generate your HTML matrix
<- generate_html(q1_code,q1_source,opt1_value,opt1_source) q1m
Let’s break down the code step-by-step to understand its purpose in generating a survey questionnaire.
generate_html
Function
Purpose: The core of the code. It dynamically creates the HTML structure required for radio-button style survey questions.
Logic:
- It takes the provided question and option data as input.
- Uses nested sapply loops to iterate through questions and answer options.
- The sprintf function carefully formats HTML strings for each radio button, embedding codes, names, and values for later use in Shiny.
- It organizes the generated HTML into a matrix for convenient structure, with rows representing questions and columns representing answer choices.
Generating Input Matrix
q1m <- generate_html(...)
: Here, the generate_html function is called with the survey data. The resulting HTML matrix is stored in the q1m variable.
How It Works in Shiny
While this code snippet doesn’t represent a complete Shiny app, here’s how it likely fits into the bigger picture:
- UI Definition: In your Shiny UI, you’d use functions like htmlOutput() or potentially DT::datatable() to insert the contents of the q1m variable into your app’s layout, rendering the survey questions.
- Server Logic: The Shiny server would handle user selections from the radio buttons. This would involve accessing the values of the selected inputs, which can then be processed, stored, or used to trigger other actions in your app.
// app.js
$(document).ready(function() {
console.log("Document ready and script loaded."); // Debug log
$(document).on('change', '.my-radio-button', function() {
var name = $(this).attr('name');
var value = $(this).val();
console.log("Radio button changed:", name, value); // Debug log
.setInputValue(name, value);
Shiny;
}); })
The JavaScript Connection (app.js)
The JavaScript code plays a vital role connecting user actions on the survey radio buttons to Shiny’s reactivity:
$(document).on('change', '.my-radio-button', function() { ... });
: This listens for “change” events on any elements with the class my-radio-button (which your radio buttons have).Shiny.setInputValue(name, value);
: The key line! It tells Shiny to update an input variable with the name and value of the selected radio button. This triggers Shiny’s reactivity, potentially causing other parts of your app to update.
style.css is following.
.gowun-dodum-regular {
font-family: 'Gowun Dodum', sans-serif;
font-weight: 400;
font-style: normal;
}
body {font-family: 'Gowun Dodum', sans-serif;
}
.h3-center {
text-align: center;
font-weight: bold;
}.input-row {
padding: 5px 0;
}.shiny-input-container {
padding: 0 5px;
}.shiny-input-container>label {
font-weight: bold;
}.shiny-input-radio>label {
font-weight: normal;
}
.table-like {
border: 1px solid #ff4d4d; /* Color of your table border */
background-color: #e6f2ff; /* Light blue background for input fields */
border-radius: 5px; /* Rounded corners for the 'table' */
}.table-header {
background-color: #ff4d4d; /* Red background for headers */
color: white; /* Text color for headers */
text-align: center;
border-radius: 5px 5px 0 0; /* Rounded corners for header */
padding: 5px;
margin-bottom: 0; /* Remove bottom margin from headers */
}.inline label{
display: table-cell; text-align: left; vertical-align: middle;
} .inline .form-group{display: table-row;}
.input-large {
width: 100%;
max-width: 100%;
height: 150px;
}.form-title {
background-color: #e9ecef;
padding: 10px;
font-weight: bold;
#border-top: 2px solid #0275d8;
#border-bottom: 2px solid #0275d8;
margin-bottom: 20px;
}
.vertical-align-middle {
display: inline-block;
vertical-align: middle;
}
.inline-radio-group .shiny-input-radio {
display: inline-block;
vertical-align: top;
margin-right: 5px;
}.inline-radio-group label {
display: inline-block;
margin-right: 20px;
}.inline-radio-group .form-group {
margin-bottom: 0;
}.shiny-input-container {
clear: both;
padding-top: 5px;
}
.dataTable thead th {
tablebackground-color: #e9ecef;
border: 0.1px solid #d3d3d3;
}
.custom-row .row {
margin-bottom: 2px; /* Reduces space between rows */
}
.custom-row .form-group {
margin-bottom: 2px; /* Reduces bottom margin of form groups, affecting radio buttons */
}
.action-button {
border: 1px solid #ff4d4d; /* Color of your table border */
background-color: #e6f2ff; /* Light blue background for input fields */
border-radius: 5px; /* Rounded corners for the 'table' */
}
You can find full code of ui, server, global here