Related
I am trying to bring 4 boxes per row, into my shiny app. BioTuring and Scope I want aligned on the same row as FLAT and GTEx. This is how it looks now:
One remark, this app is done with golem structure. Thus, please bear in mind when helping and go with the structure I have.
Having said that I have thumbnail_label
thumbnail_label <- function(url, image, label="", content="", tool="misc",
category = "tool") {
tags$a(
href = url,
onclick = paste0("gtag('event', 'click', { 'event_category': '", category,
"', 'event_label': '", tool, "'});"),
target = "_blank",
div(class = "row",
div(class = "col-sm-14 col-md-12",
div(class = "thumbnail",
img(src = image, alt = "...", height = 200, width = 100,
div(class = "caption", h5(label), p(content))
)
)
)
)
)
}
thumnail_label is brought into a module (as per golem). This is my module for gene expressions. I have the feeling this is where I can correct to bring the 2 boxes aligned into one row, alongside FLAT and GTEx.
mod_gene_expressions_sign_path_ui <- function(id){
ns <- NS(id)
tagList(
shinydashboard::tabItem(
tabName = "gene_app",
fluidRow(
shiny::headerPanel(h2("Gene Analysis")),
br(),
column(
3,
thumbnail_label(
url = "https://rstudio-connect.RStudio_FLAT/",
image = "www/Fluidigm.v2.png",
tool = "Fludigm_Browser",
label = "Fludigm Browser",
content = "Perform Fluidigm data analysis"
)
),
br())))
Then gene module above is going into app_ui (as golem structure) see bellow. However, to bring 4 boxes onto the row should not happen in the bellow code, but above.
app_ui <- function(request) {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
# Your application UI logic
shinyUI(
navbarPage(
windowTitle = "Page",
title = div(img(src = ".png", height = "30px"), "Toolbox"),
theme = shinythemes::shinytheme("cerulean"),
tabPanel("Toolbox", icon = icon("wrench"),
shinydashboard::dashboardPage(
header = shinydashboard::dashboardHeader(title = " ", titleWidth = 300),
shinydashboard::dashboardSidebar(
width = 300 ,
shinydashboard::sidebarMenu(
shinydashboard::menuItem(
"Tools",
tabName = "tools_app",
icon = icon("wrench"),
shinydashboard::menuSubItem(
"Gene /Pathways",
tabName = "gene_app",
icon = icon("chart-line")
),
shinydashboard::menuSubItem(
"Genomic",
tabName = "genomic_app",
icon = icon("universal-access")
),
shinydashboard::dashboardBody(
shinydashboard::tabItems(
shinydashboard::tabItem("tools_app", mod_tools_path_ui("tools_path_ui_1")),
shinydashboard::tabItem("gene_app",mod_gene_expressions_sign_path_ui("gene_expression_sign_path_ui_1")),
shinydashboard::tabItem("genomic_app", mod_genomic_ui("genomic_ui_1")),
tabPanel(
"Tutorials", icon = icon("graduation-cap")),
tabPanel("Worflows", icon = icon("list"))
)))
}
Can someone help me, please?
Keeping golem structure, I have managed to bring the 4 boxes into a row by getting rid of shiny::headerPanel(h2("")),
and this is the final code:
mod_gene_expressions_sign_path_ui <- function(id){
ns <- NS(id)
tagList(
shinydashboard::tabItem(
tabName = "gene_app",
# column(width = 9,
fluidRow(
shiny::headerPanel(h2("Gene Expression Analysis")),
br(),
column(
3,
thumbnail_label(
url = "https://rstudio-connect.scp.astrazeneca.net/RStudio_FLAT/",
image = "www/FluidigmAnalysisToolkit.v2.png",
tool = "Fludigm_Browser",
label = "Fludigm Browser",
content = "Perform Fluidigm data analysis"
)
),
column(
3,
thumbnail_label(
url = "https://gtexportal.org/home",
image = "www/gtex.png",
tool = "GTEx",
label = "GTEx Portal",
content = "Gene expression in normal tissue"
)
),
# shiny::headerPanel(h2("")),
column(
3,
thumbnail_label(
url = "https://azcollaboration.sharepoint.com/:b:/r/sites/BioinformaticsfortheBench/Shared%20Documents/Tools/BioTuring/BioTuring_Installation_Instructions.v2021.5.17.pdf?csf=1&web=1&e=TVpy8S",
image = "www/bioturing.svg",
content = "Platform for single-cell analysis and spatial transcriptomics exploration",
label = "BioTuring",
tool = "BioTuring"
)
),
column(
3,
thumbnail_label(
url = "http://informatics.medimmune.com/shiny/scope/",
image = "www/scope.svg",
content = "Explore available single cell RNA-Seq studies",
label = "SCOPE",
tool = "SCOPE"
)
),
br(),
shiny::headerPanel(h2("Pathway Analysis")),
br(),
column(
3,
thumbnail_label(
url = "https://clarivate.com/cortellis/learning/clarivate-for-astrazeneca1796/",
image = "www/clarivate.png",
tool = "clarivate",
label = "Clarivate",
content = "Pathway analysis tools from Cortellis including MetaCore"
)
),
column(
3,
thumbnail_label(
url = "https://analysis.ingenuity.com/pa/launch.jsp",
image = "www/ipa.png",
tool = "IPA",
label = "Ingenuity Pathway Analysis",
content = "Analyze data using manually curated gene sets"
)
),
column(
3,
thumbnail_label(
url = "https://astrazeneca.onramp.bio",
image = "www/onramp.png",
tool = "OnRamp",
label = "OnRamp - Rosalind",
content = "Interactively explore RNA-seq and ChIP-Seq data"
)
),
br(),
column(
3,
thumbnail_label(
url = "http://software.broadinstitute.org/gsea/msigdb/index.jsp",
image = "www/gsea.png",
tool = "GSEA",
label = "GSEA",
content = "Gene set enrichment analysis"
)
)
)
)
)
}
I have a shiny app, fully built, that I had built all in one script. Everything was working, the ui, etc. and I wanted to modularize the app to make the code a bit more readable. Now all the plots render on top of each other and I can't figure out why. I've tried using pageContainer, sidbarLayout, sidePanel and mainPanel, fluidPage, fixedPage, etc. and none have worked. I'm also using pagePiling with my main ui, so not sure if that has sometimes to do with it? Any help would be much appreciated!
Please find the full code here: https://github.com/eoefelein/COVID_Business_Recovery_and_Social_Capital/tree/master/socialCapitalEmployment
Here is what my ui code looks like:
ui <- tagList(
pagePiling(
center = TRUE,
sections.color = c("#3333FF", "#E6E6E6"),
menu = c(
"Home" = "home",
"Map" = "map",
"Series" = "ts",
"PCA" = "pca",
"Predict" = "predict",
"About" = "about"
),
pageSectionImage(
center = TRUE,
img = "",
menu = "home",
h1(("title"), class = "header shadow-dark"),
h3(
class = "light footer",
"by",
tags$a("news-r", href = "https://news-r.org", class = "link")
)
),
pageSection(center = TRUE,
menu = "map",
mod_map_ui("map"),
br()),
pageSection(center = TRUE,
menu = "ts",
mod_ts_ui("ts"),
br()),
pageSection(center = TRUE,
menu = "pca",
mod_pca_ui("pca"),
br()),
pageSection(
center = TRUE,
menu = "predict",
mod_predict_ui("predict"),
),
pageSection(
center = TRUE,
menu = "about",
h1("About", class = "header shadow-dark"),
h2(
class = "shadow-light",
tags$a(
"The code",
href = "https://github.com/news-r/fopi.app",
target = "_blank",
class = "link"
),
"|",
tags$a(
"The API",
href = "https://github.com/news-r/fopi",
target = "_blank",
class = "link"
)
),
h3(
class = "light footer",
"by",
tags$a("news-r", href = "https://news-r.org", class = "link")
)
)
)
)
Here is my first module's ui:
mod_map_ui <- function(id) {
ns <- NS(id)
tagList(
fluidPage(
h1("Employment & Social Capital across the U.S by County"),
center = TRUE,
column(9, leafletOutput(ns("map"), height = "100vh")),
column(
3,
shinyWidgets::radioGroupButtons(
inputId = ns("idx"),
label = "Metric",
choices = c(unique(social_indices$name)),
checkIcon = list(yes = icon("ok",
lib = "glyphicon"))
)
)
))
}
And here is my second module's ui:
mod_ts_ui <- function(id) {
ns <- NS(id)
tagList(
fixedPage(
h2("Employment by County", align = "center"),
fixedRow(
column(
4,
selectizeInput(
inputId = ns("dataset"),
label = "Choose a county:",
choices = c(unique(employment["countyfips"])),
multiple = TRUE,
selected = "Travis County, Texas",
options = list(create = TRUE)
)
),
column(
8, (echarts4r::echarts4rOutput(ns("ts_plot")))
)
)
)
)
}
okay, so I fixed it and what I think fixed it was specifying a color for each section:
pagePiling(
center = TRUE,
sections.color = c("#7b959c", "#445768","#b9bdc9","#f6e7ea","#e2e2e2","#1e4356"),
menu = c(
"Home" = "home",
"Map" = "map",
"Series" = "ts",
"PCA" = "pca",
"Predict" = "predict",
"About" = "about"
),
Very strange, but after I did that, each plot presented as it should, in it's own section. Hope this helps someone in the future!
I am trying to decrease the size of my dygraph output. But all of my settings seem to be ignored.
I have tried specifying the height in both the dashboard layout and dygraph settings. All packages and software is up-to-date via github not CRAN.
Here is what I have tried below.
I have listed my code below in DOM hierarchical order to assist with your understanding.
shinyUI(dashboardPage(header, sidebar, body))
body <- dashboardBody(
tabItems(
...
tabItem(tabName = "comments_explorer_tab", tab_comment),
...
)
)
tab_comment <- fluidPage(
titlePanel("Data Explorer")
column(
...
width = 2 # reduce width of sidebar
),
column(
width = 10,
fluidRow(height='50px',
tabsetPanel(
tabPanel("Time", height=50, dygraphs::dygraphOutput("comment_dygraph")))
),
fluidRow(DTOutput("data_table"))
)
)
shinyServer(function(input, output) {
output$comment_dygraph <- renderDygraph({
dygraph(comment_counts, height=50, main = paste0("Daily Counts for "user")) %>%
dyOptions(colors = RColorBrewer::brewer.pal(3, "Dark2"), includeZero = TRUE, retainDateWindow = TRUE) %>%
dyAxis("x", drawGrid = FALSE) %>%
dyAxis("y", label = "Counts")
})
output$data_table <- renderDT({
datatable(data = comment_time_selection(),
extensions = c("Scroller"),
options = list(
autoWidth = TRUE,
scrollResize =TRUE,
scrollX = TRUE,
scrollCollapse= TRUE,
scrollY = 100,
initComplete = I('function(setting, json) { alert("done"); }')
),
rownames= FALSE
)
})
}
I'm trying to scaffold together a basic dashboard using the shinymaterial package (https://ericrayanderson.github.io/shinymaterial/) but having a slight issue where my dropdown menus (usually selectInput in regular shiny apps) don't show up in a nested UI module.
There should be two dropdown menus above the "Settings" button in this screenshot:
Here's the code snippet for my scaffolding so far:
library(shiny)
library(shinymaterial)
# Wrap shinymaterial apps in material_page
ui <- material_page(
title = "App Title",
nav_bar_fixed = FALSE,
nav_bar_color = "black",
background_color = "white",
# font_color = "black",
# Place side-nav in the beginning of the UI
material_side_nav(
fixed = FALSE,
# Place side-nav tabs within side-nav
material_side_nav_tabs(
side_nav_tabs = c(
"Home" = "home",
"About" = "about"
),
icons = c("home", "help")
),
background_color = "white"
),
# Define side-nav tab content
material_side_nav_tab_content(
side_nav_tab_id = "home",
material_row(
material_column(
material_card(title = NULL,
sidebarCharts("main"),
depth = NULL),
width = 2,
offset = 0
),
material_column(
material_card(title = NULL,
"Chart goes here",
depth = NULL),
width = 10,
offset = 0
)
)
),
material_side_nav_tab_content(
side_nav_tab_id = "about",
tags$h1("About")
)
)
server <- function(input, output, session) {
callModule(chartSettings, "main")
}
# Server modules
chartSettings <- function(input, output, session) {
## 'Home' tab -- Sidebar
output$selectRootSymbol <- renderUI({
.choices <- c('a','b','c')
tagList(
helpText("Root Symbol:"), # Note: helpText() looks a little cleaner versus using the 'label' parameter in selectInput() below
# selectInput(session$ns("reactiveRootSymbol"), label = NULL, choices = .choices, selected = NULL, width = '100%')
material_dropdown(session$ns("reactiveRootSymbol"), label = NULL, choices = .choices, selected = NULL, width = '100%')
)
})
output$selectSymbol <- renderUI({
req(input$reactiveRootSymbol)
.choices <- c('d', 'e', 'f')
tagList(
helpText("Symbol:"),
# selectInput(session$ns("reactiveSymbol"), label = NULL, choices = toupper(.choices), selected = NULL, width = '100%')
material_dropdown(session$ns("reactiveSymbol"), label = NULL, choices = toupper(.choices), selected = NULL, width = '100%')
)
})
}
sidebarCharts <- function(id) {
ns <- NS(id)
tagList(
uiOutput(ns("selectRootSymbol")),
uiOutput(ns("selectSymbol")),
# actionButton(ns("settings"), "Settings", icon = icon("cogs"), width = '100%', class = "btn btn-primary"),p()
material_button(ns("settings"), "Settings", icon = "settings")
)
}
shinyApp(ui = ui, server = server)
I think I have a namespace issue, but I'm not sure (since the button does show up in the nested module). What am I doing wrong?
Any help is much appreciated!
There are at least two issues here.
1. material_dropdown does not display (resolved)
This appears to be due to the unused width = 100% option inside material_dropdown(). Removing this results in some of the drop downs displaying and all of the labels displaying.
2. Consecutive material_dropdown does not display (unresolved)
Having two consecutive material_dropdown's results in only the first drop down displaying, even though both labels display. There have been previous bugs with material_dropdown in the shinymaterial package so this could be part of a related issue.
Here is the code following my exploration:
library(shiny)
library(shinymaterial)
# submodule UI
sidebarCharts <- function(id) {
ns <- NS(id)
tagList(
uiOutput(ns("selectRootSymbol")),
uiOutput(ns("selectSymbol")),
# actionButton(ns("settings"), "Settings", icon = icon("cogs"), width = '100%', class = "btn btn-primary"),p()
material_button(ns("settings"), "Settings", icon = "settings")
)
}
# submodule server
chartSettings <- function(input, output, session) {
## 'Home' tab -- Sidebar
output$selectRootSymbol <- renderUI({
.choices <- c('a','b','c')
material_dropdown(session$ns("reactiveRootSymbol"), label = "Root Symbol:", choices = .choices)
})
output$selectSymbol <- renderUI({
# req(input$reactiveRootSymbol)
.choices <- c('d', 'e', 'f')
material_dropdown(session$ns("reactiveSymbol"), label = "Symbol:", choices = .choices)
})
}
## Wrap shinymaterial apps in material_page ----
ui <- material_page(
title = "App Title",
nav_bar_fixed = FALSE,
nav_bar_color = "black",
background_color = "white",
# font_color = "black",
# Place side-nav in the beginning of the UI
material_side_nav(
fixed = FALSE,
# Place side-nav tabs within side-nav
material_side_nav_tabs(
side_nav_tabs = c(
"Home" = "home"
),
icons = c("home")
),
background_color = "white"
),
# Define side-nav tab content
material_side_nav_tab_content(
side_nav_tab_id = "home",
material_row(
material_column(
material_card(title = NULL,
sidebarCharts("main"),
depth = NULL),
width = 2,
offset = 0
),
material_column(
material_card(title = NULL,
"Chart goes here",
depth = NULL),
width = 10,
offset = 0
)
)
)
)
## main server ----
server <- function(input, output, session) {
callModule(chartSettings, "main")
}
## run ----
shinyApp(ui = ui, server = server)
This question might seem to be a duplicate, but let me explain why it's not.
I want to create a shiny navbarPage that has fixed elements and a reactive number of tabPanels, that reacts to other input elements. There are many questions about how to create reactive tabsetPanels/navbarPages but they mostly aim for what it has to look like. The most common answer (and the answer i don't seek) is to render the whole navbarPage with updated set of tabPanels. I am aware of that concept and I used it in the code below.
Here is what I want my app to look like:
library(shiny)
runApp(
shinyApp(
ui = shinyUI(
fluidPage(
uiOutput("navPage")
)
),
server = function(input, output, session){
MemoryValue1 <- 1
MemoryValue2 <- 1
makeReactiveBinding("MemoryValue1")
observeEvent(input$button, {
output[[paste0("plot_", input$number)]] <- renderPlot({
hist(rnorm(1000))
})
})
observeEvent(input$insidepanels, {
MemoryValue1 <<- input$insidepanels
})
observeEvent(input$number, {
MemoryValue2 <<- input$number
})
output$navPage <- renderUI({
OutsidePanel1 <- tabPanel("Outside1",
numericInput("insidepanels", label = "Number of panels inside NavMenu", value = isolate(MemoryValue1), step = 1, min = 1),
numericInput("number", label = "Panel to add Output-Element to", value = 1, step = isolate(MemoryValue2), min = 1),
actionButton("button", label = "Add Output-Element")
)
OutsidePanel2 <- tabPanel("Ouside2", "Outside 2")
InsidePanels <- lapply(1:MemoryValue1, function(x){tabPanel(paste0("Inside", x), plotOutput(paste0("plot_", x)))})
do.call(navbarPage, list("Nav", OutsidePanel1, OutsidePanel2, do.call(navbarMenu, c("Menu", InsidePanels))))
})
}
)
)
As you might have seen, it takes a lot of effort to store your input values if they are inside other panels and will be re-rendered = reset all the time. I find this solution to be illegible and slow, because of unnecessary rendering. It also interrupts the user who is clicking through values of input$insidepanels.
What I want the app to be like is that the Outside Panels are fixed and dont re-render. The main problem is that inside shiny, navbarPage on rendering distributes HTML elements to two different locations. Inside the navigation panel and to the body as tab content. That means a-posteori added elements will not be properly embedded.
So far, I have tried to create the navbarPage with custom tags and have dynamic output alter only parts of it. That works pretty well with the navigation panel, but not with tab contents. The reason is that all tabs (their div containers) are listed one after another and as soon as I want to inject multiple at once, I am offthrown by htmlOutput, since it (seemingly) has to have a container and cannot just deliver plain HTML. Thus, all custom tabs are not recongnized properly.
Here my code so far:
library(shiny)
runApp(
shinyApp(
ui = shinyUI(
fluidPage(
tags$nav(class = "navbar navbar-default navbar-static-top", role = "navigation",
tags$div(class = "container",
tags$div(class = "navbar-header",
tags$span(class = "navbar-brand", "Nav")
),
tags$ul(class = "nav navbar-nav",
tags$li(
tags$a(href = "#tab1", "data-toggle" = "tab", "data-value" = "Outside1", "Outside1")
),
tags$li(
tags$a(href = "#tab2", "data-toggle" = "tab", "data-value" = "Outside2", "Outside2")
),
tags$li(class = "dropdown",
tags$a(href = "#", class = "dropdown-toggle", "data-toggle" = "dropdown", "Menu1"),
htmlOutput("dropdownmenu", container = tags$ul, class = "dropdown-menu")
)
)
)
),
tags$div(class = "container-fluid",
tags$div(class = "tab-content", id = "tabContent",
tags$div(class = "tab-pane active", "data-value" = "Outside1", id = "tab1",
numericInput("insidepanels", label = "Number of panels inside NavMenu", value = 1, step = 1, min = 1),
numericInput("number", label = "Panel to add Output-Element to", value = 1, step = 1, min = 1),
actionButton("button", label = "Add Output-Element")
),
tags$div(class = "tab-pane", "data-value" = "Outside2", id = "tab2", "Content 2"),
htmlOutput("tabcontents")
)
)
)
),
server = function(input, output, session){
observeEvent(input$button, {
output[[paste0("plot_", input$number)]] <- renderPlot({
hist(rnorm(1000))
})
})
output$dropdownmenu <- renderUI({
lapply(1:input$insidepanels, function(x){tags$li(tags$a(href = paste0("#tab-menu-", x), "data-toggle" = "tab", "data-value" = paste0("Inside", x), paste("Inside", x)))})
})
output$tabcontents <- renderUI({
tagList(
lapply(1:input$insidepanels, function(x){div(class = "tab-pane", "data-value" = paste("Inside", x), id = paste0("tab-menu-", x), plotOutput(paste0("plot_", x)))})
)
})
}
)
)
Note: I also tried to create HTML with JavaScript-Chunks that is triggered from inside server. This works for simple tab content, but I want my tabPanels to still have shiny output elements. I don't see how I can fit that in with JavaScript. That is why I included the plotOutput content in my code.
Thanks to anybody who can help solve this issue!
Finally came up with an own answer. I hope this can be a useful reference to others who try to understand shiny reactiveness. The answer is JavaScript for custom elements (rebuilding standard shiny elements) and using Shiny.unbindAll() / Shiny.bindAll() to achieve the reactivity.
Code:
runApp(
shinyApp(
ui = shinyUI(
fluidPage(
tags$script('
Shiny.addCustomMessageHandler("createTab",
function(nr){
Shiny.unbindAll();
var dropdownContainer = document.getElementById("dropdown-menu");
var liNode = document.createElement("li");
liNode.setAttribute("id", "dropdown-element-" + nr);
var aNode = document.createElement("a");
aNode.setAttribute("href", "#tab-menu-" + nr);
aNode.setAttribute("data-toggle", "tab");
aNode.setAttribute("data-value", "Inside" + nr);
var textNode = document.createTextNode("Inside " + nr);
aNode.appendChild(textNode);
liNode.appendChild(aNode);
dropdownContainer.appendChild(liNode);
var tabContainer = document.getElementById("tabContent");
var tabNode = document.createElement("div");
tabNode.setAttribute("id", "tab-menu-" + nr);
tabNode.setAttribute("class", "tab-pane");
tabNode.setAttribute("data-value", "Inside" + nr);
var plotNode = document.createElement("div");
plotNode.setAttribute("id", "plot-" + nr);
plotNode.setAttribute("class", "shiny-plot-output");
plotNode.setAttribute("style", "width: 100% ; height: 400px");
tabNode.appendChild(document.createTextNode("Content Inside " + nr));
tabNode.appendChild(plotNode);
tabContainer.appendChild(tabNode);
Shiny.bindAll();
}
);
Shiny.addCustomMessageHandler("deleteTab",
function(nr){
var dropmenuElement = document.getElementById("dropdown-element-" + nr);
dropmenuElement.parentNode.removeChild(dropmenuElement);
var tabElement = document.getElementById("tab-menu-" + nr);
tabElement.parentNode.removeChild(tabElement);
}
);
'),
tags$nav(class = "navbar navbar-default navbar-static-top", role = "navigation",
tags$div(class = "container",
tags$div(class = "navbar-header",
tags$span(class = "navbar-brand", "Nav")
),
tags$ul(class = "nav navbar-nav",
tags$li(
tags$a(href = "#tab1", "data-toggle" = "tab", "data-value" = "Outside1", "Outside1")
),
tags$li(
tags$a(href = "#tab2", "data-toggle" = "tab", "data-value" = "Outside2", "Outside2")
),
tags$li(class = "dropdown",
tags$a(href = "#", class = "dropdown-toggle", "data-toggle" = "dropdown", "Menu1"),
tags$ul(id = "dropdown-menu", class = "dropdown-menu")
)
)
)
),
tags$div(class = "container-fluid",
tags$div(class = "tab-content", id = "tabContent",
tags$div(class = "tab-pane active", "data-value" = "Outside1", id = "tab1",
numericInput("insidepanels", label = "Number of panels inside NavMenu", value = 0, step = 1),
numericInput("number", label = "Panel to add Output-Element to", value = 0, step = 1),
actionButton("button", label = "Add Output-Element")
),
tags$div(class = "tab-pane", "data-value" = "Outside2", id = "tab2", "Content 2")
)
)
)
),
server = function(input, output, session){
allOpenTabs <- NULL
observeEvent(input$insidepanels, {
if(!is.na(input$insidepanels)){
localList <- 0:input$insidepanels
lapply(setdiff(localList, allOpenTabs), function(x){
session$sendCustomMessage(type = "createTab", message = x)
})
lapply(setdiff(allOpenTabs, localList), function(x){
session$sendCustomMessage(type = "deleteTab", message = x)
})
allOpenTabs <<- localList
}
})
observeEvent(input$button, {
output[[paste0("plot-", input$number)]] <- renderPlot({
hist(rnorm(1000))
})
})
}
), launch.browser = TRUE
)
It is basically adding the HTML Elements "by hand" and linking them to shiny listeners.