Tooltip in shiny UI for help text - r

I want to place a help text for check-box label as a tooltip.
In the following example I use the shinyBS package - but I only get it to work for the title of the checkbox input group.
Any ideas how it could work after the "Lernerfolg" or "Enthusiasmus" labels?
library(shiny)
library(shinyBS)
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
output$rendered <- renderUI({
checkboxGroupInput("qualdim", tags$span("Auswahl der Qualitätsdimension",
tipify(bsButton("pB2", "?", style = "inverse", size = "extra-small"),
"Here, I can place some help")),
c("Lernerfolg" = "Lernerfolg" ,
"Enthusiasmus" = "Enthusiasmus"
),
selected = c("Lernerfolg"))
})
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
uiOutput("rendered")
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)

Sadly, this is one of these moments, where shiny hides most of the construction, which makes it hard to get what you want into the right places.
But like most of the time, some JavaScript will do the trick. I wrote you a function that inserts the bsButton in the right place and calls a shinyBS function to insert the tooltip. (I mainly reconstructed what tipify and bdButton would have done.) With the function you can modifify your tooltip easily without further knowledge about JavaScript.
If you'd like to know more of the details, just ask in comments.
Note: When you refer to the checkbox, use the value of it (the value that is sent to input$qualdim)
library(shiny)
library(shinyBS)
server <- function(input, output) {
makeCheckboxTooltip <- function(checkboxValue, buttonLabel, Tooltip){
script <- tags$script(HTML(paste0("
$(document).ready(function() {
var inputElements = document.getElementsByTagName('input');
for(var i = 0; i < inputElements.length; i++){
var input = inputElements[i];
if(input.getAttribute('value') == '", checkboxValue, "'){
var buttonID = 'button_' + Math.floor(Math.random()*1000);
var button = document.createElement('button');
button.setAttribute('id', buttonID);
button.setAttribute('type', 'button');
button.setAttribute('class', 'btn action-button btn-inverse btn-xs');
button.appendChild(document.createTextNode('", buttonLabel, "'));
input.parentElement.parentElement.appendChild(button);
shinyBS.addTooltip(buttonID, \"tooltip\", {\"placement\": \"bottom\", \"trigger\": \"hover\", \"title\": \"", Tooltip, "\"})
};
}
});
")))
htmltools::attachDependencies(script, shinyBS:::shinyBSDep)
}
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
output$rendered <- renderUI({
list(
checkboxGroupInput("qualdim", tags$span("Auswahl der Qualitätsdimension",
tipify(bsButton("pB2", "?", style = "inverse", size = "extra-small"), "Here, I can place some help")),
choices = c("Lernerfolg" = "Lernerfolg", "Enthusiasmus" = "Enthusiasmus"),
selected = c("Lernerfolg")),
makeCheckboxTooltip(checkboxValue = "Lernerfolg", buttonLabel = "?", Tooltip = "Look! I can produce a tooltip!")
)
})
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
uiOutput("rendered")
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
Edit:
Added the ShinyBS Dependencies such that the JavaScript API for shinyBS is loaded into the WebSite. Before, this was (more or less accidentally) happening because of the other call to bsButton.
Edit Nr.2: Much more In-Shiny
So this JavaScript thing is quite nice, but is kinda prone to errors and demands the developer to have some additional language skills.
Here, I present another answer, inspired by #CharlFrancoisMarais , that works only from within R and makes things more integrated than before.
Main things are: An extension function to the checkboxGrouInput that allows for adding any element to each of the Checkbox elements. There, one can freely place the bsButton and tooltips, like you would in normal markup, with all function arguments supported.
Second, an extension to the bsButton to place it right. This is more of a custom thing only for #CharlFrancoisMarais request.
I'd suggest you read the Shiny-element manipulation carefully, because this offers so much customization on R level. I'm kinda exited.
Full Code below:
library(shiny)
library(shinyBS)
extendedCheckboxGroup <- function(..., extensions = list()) {
cbg <- checkboxGroupInput(...)
nExtensions <- length(extensions)
nChoices <- length(cbg$children[[2]]$children[[1]])
if (nExtensions > 0 && nChoices > 0) {
lapply(1:min(nExtensions, nChoices), function(i) {
# For each Extension, add the element as a child (to one of the checkboxes)
cbg$children[[2]]$children[[1]][[i]]$children[[2]] <<- extensions[[i]]
})
}
cbg
}
bsButtonRight <- function(...) {
btn <- bsButton(...)
# Directly inject the style into the shiny element.
btn$attribs$style <- "float: right;"
btn
}
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
output$rendered <- renderUI({
extendedCheckboxGroup("qualdim", label = "Checkbox", choiceNames = c("cb1", "cb2"), choiceValues = c("check1", "check2"), selected = c("check2"),
extensions = list(
tipify(bsButtonRight("pB1", "?", style = "inverse", size = "extra-small"),
"Here, I can place some help"),
tipify(bsButtonRight("pB2", "?", style = "inverse", size = "extra-small"),
"Here, I can place some other help")
))
})
})
}
ui <- fluidPage(
shinyjs::useShinyjs(),
tags$head(HTML("<script type='text/javascript' src='sbs/shinyBS.js'></script>")),
# useShinyBS
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
uiOutput("rendered")
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)

Here is slight change - to add tooltips only to the checkboxes.
library(shiny)
library(shinyBS)
server <- function(input, output) {
makeCheckboxTooltip <- function(checkboxValue, buttonLabel, buttonId, Tooltip){
tags$script(HTML(paste0("
$(document).ready(function() {
var inputElements = document.getElementsByTagName('input');
for(var i = 0; i < inputElements.length; i++) {
var input = inputElements[i];
if(input.getAttribute('value') == '", checkboxValue, "' && input.getAttribute('value') != 'null') {
var button = document.createElement('button');
button.setAttribute('id', '", buttonId, "');
button.setAttribute('type', 'button');
button.setAttribute('class', 'btn action-button btn-inverse btn-xs');
button.style.float = 'right';
button.appendChild(document.createTextNode('", buttonLabel, "'));
input.parentElement.parentElement.appendChild(button);
shinyBS.addTooltip('", buttonId, "', \"tooltip\", {\"placement\": \"right\", \"trigger\": \"click\", \"title\": \"", Tooltip, "\"})
};
}
});
")))
}
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
output$rendered <- renderUI({
checkboxGroupInput("qualdim",
label = "Checkbox",
choiceNames = c("cb1", "cb2"),
choiceValues = c("check1", "check2"),
selected = c("check2"))
})
output$tooltips <- renderUI({
list(
makeCheckboxTooltip(checkboxValue = "check1", buttonLabel = "?", buttonId = "btn1", Tooltip = "tt1!"),
makeCheckboxTooltip(checkboxValue = "check2", buttonLabel = "?", buttonId = "btn2", Tooltip = "tt2!")
)
})
})
}
ui <- fluidPage(
shinyjs::useShinyjs(),
tags$head(HTML("<script type='text/javascript' src='sbs/shinyBS.js'></script>")),
# useShinyBS
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
uiOutput("rendered"),
uiOutput("tooltips")
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)

Related

how to control the values entered in selectizeInput in Shiny, making sure they belong to a predifined list?

I would like that when user exits the selectizeInput field (clicks outside of selectizeInput), a new option is created and selected (option createOnBlur = TRUE), but I can't figure out how to control the created values to ensure they belong to the "choices" list.
In fact, I would like createOnBlur=TRUE working with create=FALSE, but this obviously doesn't work..
I have looked at selectize.js documentation and I think createFilter and/or onBlur() options could be useful but I didn't succeed in implementing it for my purpose.
Here is a reprex with an age input, I would like that when user tape e.g. "40" and then clik outside of input without pressing "Enter" (ie onBlur), the value 40 is recorded in the input, but if the user tape e.g "444", this impossible age value is not created in the list of choices :
library(shiny)
input_age <- function(mina = 0, maxa =100){
selectizeInput(inputId = "age",
label = "Age",
choices = c("choose one" = "", mina:maxa),
options = list(create = TRUE,
createOnBlur = TRUE)
)
}
ui <- shinyUI(fluidPage(
titlePanel("selectize createonblur"),
mainPanel(
input_age(mina = 20, maxa = 70)
)
))
# SERVER
server <- shinyServer(function(input, output) {
})
shinyApp(ui, server)
You can use updateSelectizeInput to check the selection made against the choices after each interaction with your input.
Please see the following:
library(shiny)
input_age <- function(mina = 0, maxa = 100){
selectizeInput(inputId = "age",
label = "Age",
choices = c("choose one" = "", mina:maxa),
options = list(create = TRUE,
createOnBlur = TRUE)
)
}
minAge <- 20
maxAge <- 70
ui <- shinyUI(fluidPage(
titlePanel("selectize createonblur"),
mainPanel(
input_age(mina = minAge, maxa = maxAge)
)
))
# SERVER
server <- shinyServer(function(input, output, session) {
observeEvent(req(input$age), {
if(length(setdiff(input$age, as.character(seq(minAge, maxAge)))) > 0){
updateSelectizeInput(session,
inputId = "age",
choices = seq(minAge, maxAge),
selected = "")
}
})
})
shinyApp(ui, server)
Update - Here is a JS approach:
library(shiny)
input_age <- function(mina = 0, maxa = 100){
selectizeInput(inputId = "age",
label = "Age",
choices = c("choose one" = "", mina:maxa),
options = list(create = TRUE,
createOnBlur = TRUE))
}
ui <- shinyUI(fluidPage(
tags$head(tags$script(HTML("
$(document).on('shiny:inputchanged', function(event) {
if (event.name === 'age') {
if (isNaN(parseInt(event.value)) || event.value > 70 || event.value < 20) {
var $select = $('#age').selectize();
var selectize = $select[0].selectize;
selectize.setValue(null, true);
}
}
});
"))),
titlePanel("selectize createonblur"),
mainPanel(
input_age(mina = 20, maxa = 70)
)
))
# SERVER
server <- shinyServer(function(input, output, session) {
})
shinyApp(ui, server)
You can supply a regular expression to the createFilter option. If the user types something which doesn't match this regular expression, then "Add ..." will not appear and it will not be possible to add this item.
library(shiny)
ui <- fluidPage(
titlePanel("selectize createonblur"),
mainPanel(
selectizeInput(
inputId = "age",
label = "Age",
choices = c("choose one" = "", 20:70),
options = list(
create = TRUE,
createOnBlur = TRUE,
createFilter = I("/^([2-6][0-9]|70)$/")
)
)
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)

Navbar/Tabset with reactive Panel number but NOT rendering everything

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.

Selecting all the check-boxes at once in Shiny

I would like to know how to select all the check-boxes at once. In my code I have Five check-boxes.
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
checkboxInput("checkbox1", label = "meanSNR", value= FALSE),
checkboxInput("checkbox2", label = "t-statistics", value = FALSE),
checkboxInput("checkbox3", label = "adjusted p-value", value = FALSE),
checkboxInput("checkbox4", label = "log-odds", value = FALSE),
checkboxInput("checkbox5", label = "All", value = FALSE)),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
I would like to know how to make it work
1) If the user selects the fifth check-box All, It should automatically select all the check-boxes. On uncheck, it should deselect all the Checkboxes.
2 ) If the user selects the first four check-boxes, it should select the fifth one All check-box too.
For condition 1) , the screen should like this
This isn't nearly as elegant as Jorel's answer, but it's a solution that uses pure shiny package code.
library(shiny)
#* make sure to include session as an argument in order to use the update functions
server <- function(input, output, session) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
#* This observer will update checkboxes 1 - 4 to TRUE whenever checkbox 5 is TRUE
observeEvent(
eventExpr = input$checkbox5,
handlerExpr =
{
if (input$checkbox5)
lapply(paste0("checkbox", 1:4),
function(x)
{
updateCheckboxInput(session, x, value = input$checkbox5)
}
)
}
)
#* This observer will set checkbox 5 to FALSE whenever any of checkbox 1-4 is FALSE
lapply(paste0("checkbox", 1:4),
function(x)
{
observeEvent(
eventExpr = input[[x]],
handlerExpr =
{
if (!input[[x]]) updateCheckboxInput(session, "checkbox5", value = FALSE)
}
)
}
)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
checkboxInput("checkbox1", label = "meanSNR", value= FALSE),
checkboxInput("checkbox2", label = "t-statistics", value = FALSE),
checkboxInput("checkbox3", label = "adjusted p-value", value = FALSE),
checkboxInput("checkbox4", label = "log-odds", value = FALSE),
checkboxInput("checkbox5", label = "All", value = FALSE)
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
Some follow up and recommendations
I spent a little time trying to get the application to do what you've specified, but honestly, it felt pretty unnatural (and wasn't working particularly well).
In a checkbox, if you check "All", it implies that you wish to check all the boxes, but I don't think unselecting "All" necessarily implies unselecting all of the boxes.
Stemming from 1), you're trying to have one control do two different things, which can open the door to confusion.
So here's my recommendation: User four checkboxes and two buttons. The two buttons control if you select all or unselect all of the boxes, and they act independently.
library(shiny)
#* make sure to include session as an argument in order to use the update functions
server <- function(input, output, session) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
#* This observer will update checkboxes 1 - 4 to TRUE whenever selectAll is clicked
observeEvent(
eventExpr = input$selectAll,
handlerExpr =
{
lapply(paste0("checkbox", 1:4),
function(x)
{
updateCheckboxInput(session = session,
inputId = x,
value = TRUE)
}
)
}
)
#* This observer will update checkboxes 1 - 4 to FALSE whenever deselectAll is clicked
observeEvent(
eventExpr = input$deselectAll,
handlerExpr =
{
lapply(paste0("checkbox", 1:4),
function(x)
{
updateCheckboxInput(session = session,
inputId = x,
value = FALSE)
}
)
}
)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
checkboxInput("checkbox1", label = "meanSNR", value= FALSE),
checkboxInput("checkbox2", label = "t-statistics", value = FALSE),
checkboxInput("checkbox3", label = "adjusted p-value", value = FALSE),
checkboxInput("checkbox4", label = "log-odds", value = FALSE),
actionButton("selectAll", label = "Select All"),
actionButton("deselectAll", label = "Deselect All")
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
I'd do this on JS side. I'd get every checkBox like this
var cbx1 = document.getElementById('checkbox1'); etc. and i'll store them in an array
i'll also have a function that will check everything :
checkEverything = function(){
cbx1.val = "true";
cbx2.val = "true";
// etc..
}
And i would bind this function on the 4th checkbox onclick event. I'd also have a function that check if every is checked like :
checkIfEverythingChecked = function(){
if(cbx1.val == true && cbx2.val == true)
cbx4.val = true;
}
And i'd bing this on the onclick event of every checkBox

conditionally output different colored text in Shiny

I would like Shiny to print out some different color text depending on the size of a vector. I was thinking something like:
output$some_text <- renderText({
if(length(some_vec) < 20){
paste("This is red text")
<somehow make it red>
}else{
paste("This is blue text")
<somehow make it blue>
...but then I realized, I'm doing this in the server, not the UI.
And, as far as I know, I can't move this conditional logic into the UI.
For example, something like this won't work in the UI:
if(length(some_vec)< 20){
column(6, tags$div(
HTML(paste("This text is ", tags$span(style="color:red", "red"), sep = ""))
)}
else{
tags$div(HTML(paste("This text is ", tags$span(style="color:blue", "blue"), sep = ""))
)}
Does anyone have any creative ideas?
Inspired by jenesaisquoi's answer I tried the following and it worked for me. It is reactive and requires no additional packages. In particular look at output$text3
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Reactive"),
sidebarLayout(
sidebarPanel(
helpText("Variables!"),
selectInput("var",
label = "Choose Variable",
choices = c("red", "blue",
"green", "black"),
selected = "Rojo"),
sliderInput("range",
label = "Range:",
min = 0, max = 100, value = c(0, 100))
),
mainPanel(
textOutput("text1"),
textOutput("text2"),
htmlOutput("text3"),
textOutput("text4")
)
)
))
server <- function(input, output) {
output$text1 <- renderText({
paste("You have selected variable:", input$var)
})
output$text2 <- renderText({
paste("You have selected range:", paste(input$range, collapse = "-"))
})
output$text3 <- renderText({
paste('<span style=\"color:', input$var,
'\">This is "', input$var,
'" written ', input$range[2],
' - ', input$range[1],
' = ', input$range[2] - input$range[1],
' times</span>', sep = "")
})
output$text4 <- renderText({
rep(input$var, input$range[2] - input$range[1])
})
}
# Run the application
shinyApp(ui = ui, server = server)
Came hunting for an answer to a similar question. Tried a simple approach that worked for my need. It uses inline html style, and htmlOutput.
library(shiny)
ui <- fluidPage(
mainPanel(
htmlOutput("some_text")
)
)
and
server <- function(input, output) {
output$some_text <- renderText({
if(length(some_vec) < 20){
return(paste("<span style=\"color:red\">This is red text</span>"))
}else{
return(paste("<span style=\"color:blue\">This is blue text</span>"))
}
})
}
Conditionals run server side--it wasn't precisely clear to me from opening question that the author needed the conditional to run in UI. I didn't. Perhaps a simple way to address the issue in common situations.
Well, I have the kernel of an idea, but I'm fairly new to anything HTML/CSS/JavaScript-related, so I'm sure it could be improved quite a bit. That said, this seems to work fairly well, as far as it goes.
The key functions are removeClass() and addClass(), which are well documented in their respective help files in shinyjs:
library(shiny)
library(shinyjs)
shinyApp(
ui = fluidPage(
useShinyjs(), ## Set up shinyjs
## Add CSS instructions for three color classes
inlineCSS(list(.red = "color: red",
.green = "color: green",
.blue = "color: blue")),
numericInput("nn", "Enter a number",
value=1, min=1, max=10, step=1),
"The number is: ", span(id = "element", textOutput("nn", inline=TRUE))
),
server = function(input, output) {
output$nn <- renderText(input$nn)
observeEvent(input$nn, {
nn <- input$nn
if(is.numeric(as.numeric(nn)) & !is.na(as.numeric(nn))) {
## Clean up any previously added color classes
removeClass("element", "red")
removeClass("element", "green")
removeClass("element", "blue")
## Add the appropriate class
cols <- c("blue", "green", "red")
col <- cols[cut(nn, breaks=c(-Inf,3.5, 6.5, Inf))]
addClass("element", col)
} else {}
})
})
It sounds like you are trying to keep it all on the client side, so you could just use a couple of conditionalPanels, which accept javascript as conditional code. For example, coloring the text in response to the current value in a numericInput box with id "len",
library(shiny)
ui <- shinyUI(
fluidPage(
fluidRow(
numericInput('len', "Length", value=19),
conditionalPanel(
condition = "$('#len').val() > 20",
div(style="color:red", "This is red!")),
conditionalPanel(
condition = "$('#len').val() <= 20",
div(style="color:blue", "This is blue!"))
)
)
)
server <- function(input, output, session) {}
shinyApp(ui = ui, server=server)
You could also add an event listener to update the text with javascript. It's kinda ugly inline (and I don't know much javascript), but you could just move the script to a file in wwww/ and use includeScript. As in the previous example, the server does nothing.
ui <- shinyUI(bootstrapPage(
numericInput('len', "Length", value=19),
div(id="divvy", style="color:blue", "This is blue!"),
tags$script(HTML("
var target = $('#len')[0];
target.addEventListener('change', function() {
var color = target.value > 20 ? 'red' : 'blue';
var divvy = document.getElementById('divvy');
divvy.style.color = color;
divvy.innerHTML = divvy.innerHTML.replace(/red|blue/g, color);
});
"))
))
Here's a more flexible answer that uses shinyjs::extendShinyjs() to give R a way to produce some parameterized JavaScript code. Compared to my other answer, the advantage of this one is that the same function can be used to reactively colorize multiple numeric outputs.
library(shiny)
library(shinyjs)
jsCode <-
"shinyjs.setCol = function(params){
var defaultParams = {
id: null,
color : 'red'
};
params = shinyjs.getParams(params, defaultParams);
$('.shiny-text-output#' + params.id).css('color', params.color);
}"
setColor <- function(id, val) {
if(is.numeric(as.numeric(val)) & !is.na(as.numeric(val))) {
cols <- c("green", "orange", "red")
col <- cols[cut(val, breaks=c(-Inf,3.5, 6.5, Inf))]
js$setCol(id, col)
}
}
shinyApp(
ui = fluidPage(
useShinyjs(), ## Set up shinyjs
extendShinyjs(text = jsCode),
numericInput("n", "Enter a number", 1, 1, 10, 1),
"The number is: ", textOutput("n", inline=TRUE),
br(),
"Twice the number is: ", textOutput("n2", inline=TRUE)
),
server = function(input, output) {
output$n <- renderText(input$n)
output$n2 <- renderText(2 * input$n)
observeEvent(input$n, setColor(id = "n", val = input$n))
observeEvent(input$n, setColor(id = "n2", val = 2 * input$n))
})

Add a page refresh button by using R Shiny

I'm making an app and I need to add a button to refresh page (same function to press F5). Is there anyone can share a piece of code to implement it?
Thanks a lot!
I do have a very simple and nice solution but it won't work for a file input.
Here's a solution that'll work for all inputs except a file input:
UPDATE 2017: this solution did not work on file inputs for the first 2 years, but it does now.
library(shiny)
library(shinyjs)
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
div(
id = "form",
textInput("text", "Text", ""),
selectInput("select", "Select", 1:5),
actionButton("refresh", "Refresh")
)
),
server = function(input, output, session) {
observeEvent(input$refresh, {
shinyjs::reset("form")
})
}
))
When you press "Refresh", all inputs will be reset to their initial values. This is what the poster said in a comment that they actually want to do.
But file inputs are very strange and it's hard to "reset" them. See here. You could hack some JavaScript together to try to almost kind of reset an input field if you want.
However, for completeness, you can also refresh the entire page. The easiest way to do that is with session$reload(). You can also do it with {shinyjs}:
library(shiny)
library(shinyjs)
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = "shinyjs.refresh_page = function() { location.reload(); }", functions = "refresh_page"),
textInput("text", "Text", ""),
actionButton("refresh", "Refresh")
),
server = function(input, output, session) {
observeEvent(input$refresh, {
shinyjs::js$refresh_page()
})
}
))
Disclaimer: both these solutions use a package I wrote, shinyjs
I have a drop-down list input:
selectInput("domain", label = h4("Domain:"), choices = Domain, selected = CurrentDomain)
The choices set is based on a table in the database. It should change after I add or delete record from the table.
When I was experimenting with your reset or refresh function, the choice set could not reflect the changes and always stay the same. However, when I use the "reload" button provided by the browser, the choice set will update immediately. I am wondering whether you have a reset/refresh solution that is equivalent to the "reload" button of the browser.
I provided my code here, which will not work but will give you an idea what I want to do.
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
Domain<-unique(SystemInfo$Domain)
Domain<-c(Domain,'NEW')
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==Domain[1]])
SubDomain<-c(SubDomain,'NEW')
CurrentDomain<-Domain[1]
CurrentSubDomain<-SubDomain[1]
SystemInfo1<-SystemInfo[SystemInfo$Domain==CurrentDomain & SystemInfo$SubDomain==CurrentSubDomain,]
jsResetCode <- "shinyjs.reset = function() {history.go(0)}"
shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::extendShinyjs(text = "shinyjs.refresh = function() { location.reload(); }"),
# div(
# id = "form",
fluidRow(
column(6, selectInput("domain", label = h4("Domain:"),
choices = Domain, selected = CurrentDomain)),
column(6,uiOutput("Condition2"))
),
# fluidRow(column(2, verbatimTextOutput("value"))),
fluidRow(
column(6, uiOutput("Condition1")),
column(6,uiOutput("Condition3"))
),
extendShinyjs(text = jsResetCode),
fluidRow(
column(2, actionButton("submit", "Save", class="btn btn-primary btn-lg")),
column(2, actionButton("cancel", "Cancel", class="btn btn-primary btn-
lg")),
column(2, actionButton("delete", "Delete", class="btn btn-primary btn-lg"))
)
#)
),
server = function(input, output) {
observeEvent(input$domain, {
if (input$domain=='NEW') {
shinyjs::disable("domain")
shinyjs::disable("delete")
CurrentSubDomain<-'NEW'
output$Condition1 = renderUI({
textInput("domainT",label = "", value = "")
})
output$Condition3 = renderUI({
textInput("subdomainT", label = "",value = "")
})
})
} else {
CurrentDomain<-input$domain
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==input$domain])
SubDomain<-c(SubDomain,'NEW')}
output$Condition2 = renderUI({
selectInput("subdomain", label = h4("SubDomain:"),
choices = SubDomain, selected =CurrentSubDomain)
})
})
observeEvent(input$subdomain, {
if (input$subdomain=='NEW') {
shinyjs::disable("domain")
shinyjs::disable("subdomain")
shinyjs::disable("delete")
output$Condition3 = renderUI({
textInput("subdomainT", label = "", value = "")
})
} else {
CurrentSubDomain<-input$subdomain
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
SystemInfo1<-SystemInfo[SystemInfo$Domain==input$domain & SystemInfo$SubDomain==input$subdomain,]
}
})
observeEvent(input$submit, {
conn<-odbcDriverConnect(connString)
DQ.DQSystemInfo<-SystemInfo[FALSE,c("Domain","SubDomain")]
DQ.DQSystemInfo[1,]<-c("","","","","","","",0,48)
DQ.DQSystemInfo$Domain<-ifelse(input$domain=='NEW',input$domainT,input$domain)
DQ.DQSystemInfo$SubDomain<-input$subdomainT
varType1 <- c("varchar(20)", "varchar(20)" )
names(varType1)<-colnames(DQ.DQSystemInfo)
sqlSave(conn, DQ.DQSystemInfo, append = TRUE, rownames = FALSE, varTypes = varType1)
close(conn)
# js$reset()
#shinyjs::reset("form")
# js$reset("form")
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
Domain<-unique(SystemInfo$Domain)
Domain<-c(Domain,'NEW')
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==Domain[1]])
SubDomain<-c(SubDomain,'NEW')
CurrentDomain<-Domain[1]
CurrentSubDomain<-SubDomain[1]
SystemInfo1<-SystemInfo[SystemInfo$Domain==CurrentDomain & SystemInfo$SubDomain==CurrentSubDomain,]
shinyjs::js$refresh()
})
observeEvent(input$cancel, {
#js$reset()
#shinyjs::reset("form")
#js$reset("form")
shinyjs::js$refresh()
})
observeEvent(input$delete, {
conn<-odbcDriverConnect(connString)
delete.query <- paste0("DELETE DQ.DQSystemInfo WHERE Domain='",
input$domain,"' and SubDomain='",input$subdomain,"'")
sqlQuery(conn, delete.query)
close(conn)
#js$reset()
# shinyjs::reset("form")
# js$reset("form")
conn<-odbcDriverConnect(connString)
SystemInfo<-sqlQuery(conn, 'SELECT * FROM [DQ].[DQSystemInfo]', stringsAsFactors = FALSE)
close(conn)
Domain<-unique(SystemInfo$Domain)
Domain<-c(Domain,'NEW')
SubDomain<-unique(SystemInfo$SubDomain[SystemInfo$Domain==Domain[1]])
SubDomain<-c(SubDomain,'NEW')
CurrentDomain<-Domain[1]
CurrentSubDomain<-SubDomain[1]
SystemInfo1<-SystemInfo[SystemInfo$Domain==CurrentDomain & SystemInfo$SubDomain==CurrentSubDomain,]
shinyjs::js$refresh()
})
},options = list(height = 520))

Resources