I was building an App with Rshiny.
I have a couple of infoBoxand I would like to use the href option to make a pop-up when clicking on the infoBox.
I use shinyBS for the popup options.
here is what i tried :
valueBox(value=entry_01, icon = icon("users","fa-lg",lib="font-awesome"),href=shinyInput(actionLink,id='button_01',len=1,class="btn btn-default action-button",label=""),
width=NULL,color = "light-blue",subtitle = ""
)
But I figured out that the href option work perfectly if we want to link on an external web site like href = "http://stackoverflow.com/"
but I didn't know how to link in an internal link of the app.
EDIT
I make this edit because i found a solution which make the box clickable and make shiny think it was an action button, by adding two variable inside the valueBox output list.
- the class action-button
- The id which allow us to use observe or observeEvent to detect when the valuebox is clicked.
Here is a reproductible example
require(shiny)
require(shinydashboard)
header <- dashboardHeader(title="ReproductibleExample")
sidebar <- dashboardSidebar(disable=T)
body <- dashboardBody(valueBoxOutput("box_01"),
textOutput("print"))
ui <- dashboardPage(header, sidebar, body)
server<-shinyServer(function(input, output,session) {
output$box_01 <- renderValueBox({
entry_01<-20
box1<-valueBox(value=entry_01
,icon = icon("users",lib="font-awesome")
,width=NULL
,color = "blue"
,href="#"
,subtitle=HTML("<b>Test click on valueBox</b>")
)
box1$children[[1]]$attribs$class<-"action-button"
box1$children[[1]]$attribs$id<-"button_box_01"
return(box1)
})
output$print<-renderText({
print(input$button_box_01)
})
})
shinyApp(ui,server)
I decided to change the method. I have now include an actionbutton (or actionLink) inside the substile element of the value box and create a bsModal element linked to this actionButton.
If you are not familiar with the ShinyBS package it allow to make popover, tooltip etc features without including HTML or java.
I follow the #Mikko Martila advice Shiny: adding addPopover to actionLink and here is a reproductile example to show you my issue :
library("shiny")
library("shinydashboard")
library("shinyBS")
header <- dashboardHeader(title = "reporductible example")
body <- dashboardBody(valueBoxOutput("box_01"),
bsModal("modal", "foo", trigger = "", "bar"))
sidebar <- dashboardSidebar()
ui <- dashboardPage(header,sidebar,body,skin="green")
server = function(input, output, session) {
# ----- First info box synthesis menu
output$box_01 <- renderValueBox({
entry_01 <- "BlaBla"
valueBox(value=entry_01, icon = icon("users",lib="font-awesome"),
width=NULL,color = "blue",subtitle = HTML("<b>my substitle</b> <button id=\"button\" type=\"button\" class=\"btn btn-default action-button\">Show modal</button>")
)
})
observeEvent(input$button, {
toggleModal(session, "modal", "open")
})
}
runApp(list(ui = ui, server = server))
I use the HTML() option to add my button inside the subtitle of value boxes.
It's not really what i wanted but it do the work.
You can do it with actionLink (it's look better) by using subtitle like this :
subtitle=HTML("<b>my subtitle</b><a id=\"button_box_05\" href=\"#\" class=\"action-button\">
<i class=\"fa fa-question-circle\"></i>
</a>")
I was stuck with the same problem and having gone through this link, just got it working, without adding a separate button, like this.
Hope this would help someone looking to solve a similar problem
require(shiny)
require(shinydashboard)
require(shinyBS)
header <- dashboardHeader(title="ReproductibleExample")
sidebar <- dashboardSidebar(disable=T)
body <- dashboardBody(valueBoxOutput("box_01"),
textOutput("print"),bsModal("mod","title","btn"))
ui <- dashboardPage(header, sidebar, body)
server<-shinyServer(function(input, output,session) {
output$box_01 <- renderValueBox({
entry_01<-20
box1<-valueBox(value=entry_01
,icon = icon("users",lib="font-awesome")
,width=NULL
,color = "blue"
,href="#"
,subtitle=HTML("<b>Test click on valueBox</b>")
)
box1$children[[1]]$attribs$class<-"action-button"
box1$children[[1]]$attribs$id<-"button_box_01"
return(box1)
})
observeEvent(input$button_box_01, {
toggleModal(session,"mod","open")
output$print<-renderText({
print(input$button_box_01)
})})
})
shinyApp(ui,server)
I know only bad variant
1) add function tags$script(HTML("function clickFunction(link){
Shiny.onInputChange('linkClicked',link);
}"))
2) edit href children of your valueBox
aa=valueBox(value="22", icon = icon("users","fa-lg",lib="font-awesome"),href="www",
width=NULL,color = "light-blue",subtitle = ""
)
aa$children[[1]]=a(href="#","onclick"=paste0("clickFunction('","click","'); return false;"),aa$children[[1]]$children)
3) observeEvent(input$linkClicked,{..})
Related
this question is a follow-up of previous post Create URL hyperlink in R Shiny? .
I'm using the solution shared there, namely:
runApp(
list(ui = fluidPage(
uiOutput("tab")
),
server = function(input, output, session){
url <- a("Google Homepage", href="https://www.google.com/")
output$tab <- renderUI({
tagList("URL link:", url)
})
})
)
The solution above works fine. However, I would need to:
customize the font size and color
make sure the link opens up a new page in a new browser tab.
I can't find a way to achieve these two goals and I'm not familiar with HTML. Any help would be much appreciated. Thanks
Use style to change font size and color and target="_blank" to open the link in new tab.
library(shiny)
runApp(
list(ui = fluidPage(
uiOutput("tab")
),
server = function(input, output, session){
url <- a("Google Homepage", href="https://www.google.com/",
style = "color:orange;font-size:18px", target="_blank")
output$tab <- renderUI({
tagList("URL link:", url)
})
})
)
There are numerous posts regarding changing titles of other pieces of Shiny apps, e.g.:
Change the title by pressing a shiny button Shiny R
Shiny page title and image
Shiny App: How to dynamically change box title in server.R?
My question is related, but not answered by any of these. I would like to make the <head><title>...</title></head> tag reactive, or at least controllable from within an observeEvent in server.R.
The following does not work, since ui can't find theTitle, but is the kind of approach I'd hope is possible:
library(shiny)
ui <- fluidPage(
title = theTitle(),
textInput("pageTitle", "Enter text:")
)
server <- function(input, output, session) {
theTitle <- reactiveVal()
observeEvent( input$pageTitle, {
if(is.null(input$pageTitle)) {
theTitle("No title yet.")
} else {
theTitle(input$pageTitle)
}
})
}
I've tried making output$theTitle <- renderText({...}) with the if..else logic in that observeEvent, and then setting title = textOutput("theTitle") in ui's fluidPage, but that generates <div ...> as the title text, or <span ...> if we pass inline=True to renderText.
In case this clarifies what I'm looking for, the answer would make something equivalent to the literal (replacing string variables with that string) ui generated by
ui <- fluidPage(
title = "No title yet.",
....
)
before the user has entered any text in the box; if they have entered "Shiny is great!" into input$pageTitle's box, then we would get the literal
ui <- fluidPage(
title = "Shiny is great!",
....
)
One way would be to write some javascript to take care of that. For example
ui <- fluidPage(
title = "No title yet.",
textInput("pageTitle", "Enter text:"),
tags$script(HTML('Shiny.addCustomMessageHandler("changetitle", function(x) {document.title=x});'))
)
server <- function(input, output, session) {
observeEvent( input$pageTitle, {
title <- if(!is.null(input$pageTitle) && nchar(input$pageTitle)>0) {
input$pageTitle
} else {
"No title yet."
}
session$sendCustomMessage("changetitle", title)
})
}
shinyApp(ui, server)
This was created following the How to send messages from the browser to the server and back using Shiny guide
As of June 2021, there is an R package called shinytitle that can update the window title from within Shiny's reactive context: https://cran.r-project.org/package=shinytitle
I am using Shiny to build a web app. I am adding a button that will show/hide some element on the page. But after the element got hide, other page componemt does not resize themselves to fill the screen. For example, I try to hide the sidebar of a sidebarLayout, by using toggle function in shinyjs. Here's the code I have:
library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(sidebarLayout(
sidebarPanel(id="sidebar"),
mainPanel(wellPanel(actionButton("sideBarControl", label = "Show/Hide")))
)))
server <- function(input, output) {
observeEvent(input$sideBarControl, {
shinyjs::toggle(id = "sidebar")
# potentially some statements here to fix the layout? But what should they be?
})
}
shinyApp(ui = ui, server = server)
The sidebar hides/shows when clicking sidebarControl button correctly, but instead of resize mainPanel to fill the screen, it shifts mainPanel to the left, and left a space at the right. How to resolve this? See the pictures below:
Let me respond to the comment to my previous suggestion. Since this is quite different from the previous, I write a new answer instead of editing it.
In general, you can inspect what HTML code is generated from your UI description by running the command on console. For example,
sidebarPanel(id="sidebar", actionButton("b", "btn"))
##<div class="col-sm-4">
## <form class="well" id="sidebar">
## <button id="b" type="button" class="btn btn-default action-button">btn</button>
## </form>
##</div>
This tells us that sidebarPanel function generates a nested framework of
div with class col-sm-4; and
form with the supplied id
The reason why your example code did not shifts the main panel is clear now; Even if you hide sidebar, which is the form inside, there still is div that encloses it.
So we would like a way to hide the div. Unfortunately, I could not find a way to do so with sidebarPanel function. An alternative is to use column function.
column(width=3, id="col", actionButton("b", "btn"))
##<div class="col-sm-3" id="col">
## <button id="b" type="button" class="btn btn-default action-button">btn</button>
##</div>
You can see that the output of column is kind of similar to that of sidebarPanel. Importantly, column allows you to give an ID to the div element.
So, here is a toy example that shifts the main panel to the left (i.e. fully hide the sidebar).
library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(fluidRow(
column(width=4, id="spcol", actionButton("dummy", "dummy")),
column(width=8, wellPanel(actionButton("sideBarControl",
label = "Show/Hide")))
)))
server <- function(input, output) {
observeEvent(input$sideBarControl, {
shinyjs::toggle(id = "spcol")
})
}
shinyApp(ui = ui, server = server)
Now, let's tackle on your second point, that is, letting the main panel to fill, instead of shifting.
mainPanel()
##<div class="col-sm-8"></div>
So it does not fill because its width is set as 8. We want to change it to 12, and we can use toggleClass function from shinyjs library for that. In short, toggleClass adds a class to an element if it does not have one, and removes the class if it already does.
I believe the code below behaves as we wish.
library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(fluidRow(
column(width=4, id="spcol", actionButton("dummy", "dummy")),
column(width=8, id="main", wellPanel(actionButton("sideBarControl",
label = "Show/Hide")))
)))
server <- function(input, output) {
observeEvent(input$sideBarControl, {
shinyjs::toggle(id = "spcol")
shinyjs::toggleClass("main", "col-sm-8")
shinyjs::toggleClass("main", "col-sm-12")
})
}
shinyApp(ui = ui, server = server)
Also, here is another working example code, in which two versions of main panels with different widths are hidden and shown by turn.
library(shiny)
library(shinyjs)
ui <- fluidPage(useShinyjs(), br(), wellPanel(fluidRow(
column(width=4, id="spcol", actionButton("dummy", "dummy")),
column(width=8, id="main1", wellPanel(actionButton("sideBarControl",
label = "Show/Hide"))),
column(width=12, id="main2", wellPanel(actionButton("sideBarControl2",
label = "Show/Hide")))
)))
server <- function(input, output) {
shinyjs::toggle(id = "main2")
observeEvent(input$sideBarControl+input$sideBarControl2, {
shinyjs::toggle(id = "spcol")
shinyjs::toggle(id = "main1")
shinyjs::toggle(id = "main2")
})
}
shinyApp(ui = ui, server = server)
How about you try shinydashboard? It has the behavior you are looking for by default.
https://rstudio.github.io/shinydashboard/get_started.html
I am using the shiny dashboard template to generate my web UI.
I'd like to dynamically generate an infobox when a computation is completed with a link directed to one of the tabItems in dashboardBody.
For example,
I can put this in my tabItem1 output,
renderInfoBox({
infoBox("Completed",
a("Computation Completed", href="#tabItem2"),
icon = icon("thumbs-o-up"), color = "green"
)
})
But the problem is that when I click the link, it does nothing. I would like it jumps to tabItem2. The link href seems valid when I hover on it.
Thanks!
Update:
Other than using Javascripts, looks like using actionLink and updateTabItems functions in shinydashboard package will work as well.
I apologize for the lengthy code sample, but I had to copy an example with tabItems from the shinydashboard homepage.
Your approach has only few problems. First, if you would inspect the menuItems, you'd see that the actual tab's id is not tabItem2, but shiny-tab-tabItem2. This, plus the extra attribute data-toggle="tab" within the a tag would suffice to open the desired tab. Snippet:
a("Computation Completed", href="#shiny-tab-tabItem2", "data-toggle" = "tab")
But, this has its limits. First and most obvious, the state of the menuItem in the sidebar is not set to active. This looks very odd and one might not be convinced, that one has been moved to another tab.
Second, and less obvious, if you listen to tab changes (on the server side), you will not get information about this tab switch. Those are triggered by the menuItem being clicked, and the tab itself will not report if it is visible or hidden.
So, my approach will be to simulate that the corresponding menuItem is clicked, and thus, all the above problems are solved.
Code example:
library(shiny)
library(shinydashboard)
ui <- shinyUI(
dashboardPage(
dashboardHeader(title = "Some Header"),
dashboardSidebar(
sidebarMenu(
menuItem("Computations", tabName = "tabItem1", icon = icon("dashboard")),
menuItem("Results", tabName = "tabItem2", icon = icon("th"))
)
),
dashboardBody(
tags$script(HTML("
var openTab = function(tabName){
$('a', $('.sidebar')).each(function() {
if(this.getAttribute('data-value') == tabName) {
this.click()
};
});
}
")),
tabItems(
tabItem(tabName = "tabItem1",
fluidRow(
box(plotOutput("plot1", height = 250)),
box(
title = "Controls",
sliderInput("slider", "Number of observations:", 1, 100, 50)
)
),
infoBoxOutput("out1")
),
tabItem(tabName = "tabItem2",
h2("Widgets tab content")
)
)
)
)
)
server <- function(input, output){
histdata <- rnorm(500)
output$plot1 <- renderPlot({
data <- histdata[seq_len(input$slider)]
hist(data)
})
output$out1 <- renderInfoBox({
infoBox("Completed",
a("Computation Completed", onclick = "openTab('tabItem2')", href="#"),
icon = icon("thumbs-o-up"), color = "green"
)
})
}
shinyApp(ui, server)
Note, that the only important thing is the onclick property, not an href. This means, that every div or other element can be used to create this link. You could even have just the thumbs-up image with this onclick command.
If you have more questions, please comment.
Best Regards
Edit: Whole infoBox clickable.
This is an answer to a comment by OmaymaS. The point was to make the infoBox a clickable container. To achieve this, one can define a new function that makes a somewhat different infoBox. The custom box will be as follows:
customInfoBox <- function (title, tab = NULL, value = NULL, subtitle = NULL, icon = shiny::icon("bar-chart"), color = "aqua", width = 4, href = NULL, fill = FALSE) {
validateColor(color)
tagAssert(icon, type = "i")
colorClass <- paste0("bg-", color)
boxContent <- div(class = "info-box", class = if (fill) colorClass,
onclick = if(!is.null(tab)) paste0("$('.sidebar a')).filter(function() { return ($(this).attr('data-value') == ", tab, ")}).click()"),
span(class = "info-box-icon", class = if (!fill) colorClass, icon),
div(class = "info-box-content",
span(class = "info-box-text", title),
if (!is.null(value)) span(class = "info-box-number", value),
if (!is.null(subtitle)) p(subtitle)
)
)
if (!is.null(href)) boxContent <- a(href = href, boxContent)
div(class = if (!is.null(width)) paste0("col-sm-", width), boxContent)
}
This code is copied from the original infoBox function definition and only the line with onclick is new. I also added the openTab function (with some twitches) right inside the container such that you dont need to worry where to put this function inside the view. Might be a bit overloaded i feel.
This custom info box can be used exactly like the default one and if you pass the additional tab argument, the link to the sidebar is added.
Edit: Subtitle exploit
As Alex Dometrius mentioned, the use of subtitle crashes this functionality. This is because the script tag that was inserted, on accident, was used as the subtitle argument in order to be rendered with the box. To free up this spot, I edited the main example up top such that the script tag is sitting top level in the dashboardBody (literally anywhere in the ui would be fine).
(To avoid confusion: in Version 1, the tags$script was supplied inside of infobox where it was interpreted as the subtitle parameter.)
Using the RStudio tutorial on dueling button, I've setup a data toggle. One of the buttons is default and so should look "active" when the page loads (with inward shadow). After clicking on the other button, the "active" state needs to switch between the two buttons.
Is it possible to implement this with Shiny?
Not sure if this is the cleanest way, but it definitely gets the job done. Got the bootstrap classes here:
library(shiny)
shinyApp(
ui = shinyUI(bootstrapPage(
uiOutput("camera_one"),
uiOutput("camera_two")
)),
server=shinyServer(function(input, output, session){
v <- reactiveValues(btn_class_c1 = NULL, btn_class_c2 = NULL)
observeEvent(input$btn1, {
v$btn_class_c1 <- "btn-primary"
v$btn_class_c2 <- "btn-default"
})
observeEvent(input$btn2, {
v$btn_class_c1 <- "btn-default"
v$btn_class_c2 <- "btn-primary"
})
output$camera_one <- renderUI({
actionButton("btn1", "Camera One", class=v$btn_class_c1)
})
output$camera_two <- renderUI({
actionButton("btn2", "Camera Two", class=v$btn_class_c2)
})
})
)