In the Shiny App below, I am facing a very strange behavior, where selectInput box slides downwards when I type something in this box. Also, the text inside selectInput box moves towards the right while I type in this box. I have spent a lot of time to find out the reason for this problem but could not figure it out. Can someone point out the mistake I am doing causing this strange behavior?
library(shiny)
library(shinydashboard)
library(highcharter)
siderbar <- dashboardSidebar(
sidebarMenu(
selectizeInput(inputId = "select_by", label = "Select by:", choices = NULL, multiple = FALSE, options = NULL)
)
)
body <- dashboardBody(
fluidRow(
tabBox(
side = "right",
selected = "Tab1",
tabPanel("Tab1", "Tab content 1", highchartOutput("tabset1Selected"))
)
),
)
shinyApp(
ui = dashboardPage(
dashboardHeader(title = "tabBoxes"),
siderbar,
body
),
server = function(input, output, session) {
selectedVal <- reactiveValues()
updateSelectizeInput(session, "select_by", choices = c(as.character(1:10000)), selected = 2, server = TRUE)
output$tabset1Selected <- renderHighchart({
selectedVal <- input$select_by
print(highcharts_demo())
})
}
)
We were on the right track. It has something to do with selectize.js updating the items from the server. You can verify that by setting the loadThrottle option to 5000. This option determines how long the widget waits "before requesting options from the server" (see the manual). Now you have to wait exactly 5 seconds and then the select widget flickers.
The issue seems to be caused by a CSS conflict. selectize.js adds a CSS class to the widget. If you remove that feature, the flicker goes away.
selectizeInput(inputId = "select_by", label = "Select by:",
choices = NULL, multiple = FALSE,
options = list(loadThrottle=200, loadingClass=""))
loadingClass sets a specific CSS class (default: 'loading') while loading data from the server. Purpose: to change how the widget looks and communicate to users that an update is in progress.
loadThrottle does not need to be set. It's default is 300. You can set it to any value that suits your needs.
Details
highcharter defines it's own CSS class names loading with these specs:
.loading {
margin-top: 10em;
text-align: center;
color: gray;
}
That is the reason for the CSS conflict. The widget gets a top margin and it's content moved to the center, because the browser does not distinguish the source of the class. It only sees some CSS that fits and uses it. This image shows where you need to look:
Related
For the dropdownMenu in the header, I want to change the icon reactively, so I have to place the code into server. However the styling goes bonkers, is there a way to keep the original styling? I've tried manually copying styles and setting everything important but it still doesn't work.
In this example, there are two dropdownMenu blocks, one in the ui (looks good) and one in the server (looks bad). I want to make the bad one look the same as the good one.
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
shinyApp(
ui = dashboardPagePlus(
header = dashboardHeaderPlus(
uiOutput("my_dropdown"),
dropdownMenu(
type = "tasks",
badgeStatus = "danger",
icon = "Looks good"
)
),
sidebar = dashboardSidebar(),
body = dashboardBody(),
rightsidebar = rightSidebar()
),
server = function(input, output) {
output$my_dropdown <- renderUI({
dropdownMenu(
type = "tasks",
badgeStatus = "danger",
icon = "Looks bad"
)
})
}
)
This is what it looks like
This is what it should look like
I have a button in my ui.R that I want to be shown only when "Summary" tab is selected, so I thought of this code
fluidRow(
column(4,
column(12,id="sub",
actionButton("submit", "SUBMIT", width = "100%"))),
column(8,
bsCollapse(id = "collapse7", open = "Results",
bsCollapsePanel("Results",
tabsetPanel(
tabPanel("Summary",
tags$script(HTML("document.getElementById('sub').style.visibility = 'visible';")))
tabPanel("Plot",
tags$script(HTML("document.getElementById('sub').style.visibility = 'hidden';"))))
))))
The problem is, the button is hidden even though in my first tab it should be visible and also when i go to Plots and back to Summary, the button stays hidden.
After looking at: How to use tabPanel as input in R Shiny?
I decided to play with observeEvent and the input$tabset option. The result is 100% working and it's really simple. Here's the code:
observeEvent(input$choices, {
choice = input$choices
if(choice == "Summary")
{
runjs(
"document.getElementById('submit').style.visibility = 'visible';"
)
}
else
{
runjs(
"document.getElementById('submit').style.visibility = 'hidden';"
)
}
})
Also, I found out why my previous code wasn't working, it was due to the fact that when the UI was initialized, the button element kept the last style modification (the hidden one) and it didn't change depending on the tab I have selected, since its not reactive.
I have a problem with shiny tabs. I want to create a navigation page with two tabs. Right to them, I would like to insert some user's login details. There is no option "text" or other to insert a text in the navbarPage. But I created an additionnal tab instead:
library(shiny)
runApp(list(
ui = navbarPage(
title="My App",
tabPanel("tab1 title"),
tabPanel("tab2 title"),
tabPanel("User: Madzia")),
server = function(input, output) { }
))
It is OK like this, but I do not want the third tab to be "selectible": I want it to be disabled, so that we cannot click on it - the same as on "My App" text. Do you have any idea about how to handle this problem?
Thank you! Best, Madzia
You can achieve disabling a tab with a tiny bit of javascript. I have an example of how to hide a tab (not disable) in recent blog post, you can see the code for that here. I modified that code a bit for disabling instead.
This code is hacky because it was done in 2 minutes but will work for a basic use case
library(shiny)
library(shinyjs)
jscode <- '
shinyjs.init = function() {
$(".nav").on("click", ".disabled", function (e) {
e.preventDefault();
return false;
});
}
'
css <- '
.disabled {
background: #eee !important;
cursor: default !important;
color: black !important;
}
'
shinyApp(
ui = fluidPage(
useShinyjs(),
extendShinyjs(text = jscode, functions = "init"),
tags$style(css),
checkboxInput("foo", "Disable tab2", FALSE),
tabsetPanel(
id = "navbar",
tabPanel(title = "tab1",
value = "tab1",
h1("Tab 1")
),
tabPanel(title = "tab2",
value = "tab2",
h1("Tab 2")
),
tabPanel(title = "tab3",
value = "tab3",
h1("Tab 3")
)
)
),
server = function(input, output) {
observe({
toggleClass(condition = input$foo,
class = "disabled",
selector = "#navbar li a[data-value=tab2]")
})
}
)
Edit I didn't fully read the question when I posted my answer, I just saw that you wanted a way to disable a tab and that was my answer. Your specific usecase (creating a tab only to show the name of a user) is a bit strange, but I suppose this will still work...
I would like to keep my previous answer in existence because it may be useful for someone in the future who wants to know how to disable a tab.
But for this specific problem, disabling the tab is not the correct approach. It makes more sense to simply add text to the tab (as Valter pointed out in a comment). If you look at the documentation for bootstrap, it says you can add text into the navbar by adding an html element with class navbar-text. I experimented with the HTML a little bit to figure out exactly where this needs to be done, and created a little function that will wrap around navbarPage() to allow you to add text to it.
Here's an example:
library(shiny)
navbarPageWithText <- function(..., text) {
navbar <- navbarPage(...)
textEl <- tags$p(class = "navbar-text", text)
navbar[[3]][[1]]$children[[1]] <- htmltools::tagAppendChild(
navbar[[3]][[1]]$children[[1]], textEl)
navbar
}
ui <- navbarPageWithText(
"Test app",
tabPanel("tab1", "tab 1"),
tabPanel("tab2", "tab 2"),
text = "User: Dean"
)
server <- function(input, output, session) {
}
shinyApp(ui = ui, server = server)
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.)
So just curious, is there any way to add a company logo to the header of a ShinyDashboard? As I am looking at the documentation, it describes changing the "logo" in the CSS, this is just configuring what goes in the upper left hand corner though as far as I can tell and I would like to keep my title there.
I am not using the drop down menus and so I would like to add my company logo on the top right where the red box is.
Does anyone have any idea how this can be done with Shinydashboard? Thanks.
Update 2020-10-27
For users that are comfortable with HTML or want more flexibility around their user interface and have access to a front end developer, I recently discovered you can use HTML to build the entire user interface. There is a Shiny article about it here. This would allow the entire branding and layout to be done in a way that could comply with your company standards if desired. Hope this helps.
I've been working with a bit of a hack for this, (and I know you didn't ask for it, but here's a clickable logo while we're at it):
library(shiny)
library(shinydashboard)
dbHeader <- dashboardHeader()
dbHeader$children[[2]]$children <- tags$a(href='http://mycompanyishere.com',
tags$img(src='logo.png',height='60',width='200'))
dashboardPage(
dbHeader,
dashboardSidebar(),
dashboardBody()
)
So this nests a shiny.tag inside the header. The second slot in this particular shiny object is the logo slot (You'll need a 'logo.png' in your /www/ folder in the app directory)
EDIT:
I just checked, and as of right now, this hack should no longer be necessary, you can insert the html directly from the dashboardHeader function via the title= parameter, (Before, that parameter was enforcing text only),
I think the answer might still be useful as a method to modify existing shiny functions where things ARE hardcoded in though.
Here's the method now:
dashboardPage(
dashboardHeader(title = tags$a(href='http://mycompanyishere.com',
tags$img(src='logo.png')))
or, adding a little more magic to the logo (I also use my logo as a loading bar):
# Takes a location 'href', an image location 'src', a loading gif 'loadingsrc'
# height, width and alt text, and produces a loading logo that activates while
# Shiny is busy
loadingLogo <- function(href, src, loadingsrc, height = NULL, width = NULL, alt = NULL) {
tagList(
tags$head(
tags$script(
"setInterval(function(){
if ($('html').attr('class')=='shiny-busy') {
$('div.busy').show();
$('div.notbusy').hide();
} else {
$('div.busy').hide();
$('div.notbusy').show();
}
},100)")
),
tags$a(href=href,
div(class = "busy",
img(src=loadingsrc,height = height, width = width, alt = alt)),
div(class = 'notbusy',
img(src = src, height = height, width = width, alt = alt))
)
)
}
dashboardBody(
dashboardHeader(title = loadingLogo('http://mycompanyishere.com',
'logo.png',
'loader.gif'),
dashboardSidebar(),
dashboardBody()
)
Here's my hack (put your logo, as has been mentioned before, into a www subdirectory of your app directory).
Because dashboardHeader() expects a tag element of type li and class dropdown, we can pass such elements instead of dropdownMenus:
library(shiny)
library(shinydashboard)
dbHeader <- dashboardHeader(title = "My Dashboard",
tags$li(a(href = 'http://shinyapps.company.com',
icon("power-off"),
title = "Back to Apps Home"),
class = "dropdown"),
tags$li(a(href = 'http://www.company.com',
img(src = 'company_logo.png',
title = "Company Home", height = "30px"),
style = "padding-top:10px; padding-bottom:10px;"),
class = "dropdown"))
server <- function(input, output) {}
shinyApp(
ui = dashboardPage(
dbHeader,
dashboardSidebar(),
dashboardBody()
),
server = server
)