I am struggling to add tooltips to html widgets in Rshiny. bs_embed_tooltip from library(flexdashboard) does the job for some shiny widgets but returns the following error when it is applied to an html widget:
Error in .tag_validate(.) :
tag is not a shiny.tag - tag must be generated using htmltools or shiny
Here is my minimal working example (modifying example code from shinydashboard):
## app.R ##
library(shinydashboard)
library(flexdashboard)
library(bsplus) # For shiny tooltips
ui <- dashboardPage(
dashboardHeader(title = "Basic dashboard"),
dashboardSidebar(),
dashboardBody(
# Boxes need to be put in a row (or column)
fluidRow(
box(plotOutput("plot1", height = 250) %>%
bs_embed_tooltip("This is the output chart.", placement = 'bottom')
),
box(title = "Controls",
sliderInput("slider", "Number of observations:", 1, 100, 50) %>%
bs_embed_tooltip("Use this slider to select the number of observations.", placement = 'bottom')
),
box(title = "Guage",
gaugeOutput("guage_value") # %>% bs_embed_tooltip("This gauge shows the input value from the slider.", placement = 'bottom')
)
)
)
)
server <- function(input, output) {
set.seed(122)
histdata <- rnorm(500)
output$plot1 <- renderPlot({
data <- histdata[seq_len(input$slider)]
hist(data)
})
output$guage_value <- renderGauge({
gauge(input$slider, min = 0, max = 100, symbol = '', gaugeSectors(
danger = c(0, 30), warning = c(31, 70), success = c(71, 100) ))
})
}
shinyApp(ui, server)
Your help to get around the code in the comment would be much appreciated.
Try with this new box for the gauge-box:
box(title = "Guage",
gaugeOutput("guage_value"),
bsTooltip(id = "guage_value", title = "This gauge shows the input value from the slider.", placement = "bottom")
)
Related
I am facing a problem with the layout in R Shiny when I use the R Markdown file inside it I get the final result in a wired layout size (small and only in the middle of the screen ) as shown in the following photo:
Attached to you the code:
library(shiny)
library(shinydashboard)
library(knitr)
ui <-
dashboardPage(
dashboardHeader(title ='Virtual Excursion'),
dashboardSidebar( sliderTextInput(
inputId = "mySliderText",
label = "Story line",
grid = TRUE,
force_edges = TRUE,
choices = c('1','2')
)
),
dashboardBody(
fluidRow(
column(9,
box(
title = "Operations ",
closable = FALSE,
width = 9,
status = "primary",
solidHeader = FALSE,
collapsible = TRUE,
uiOutput("operations")
)
)
),
fluidRow(
column(9,
box(
title = "Challenges",
closable = FALSE,
width = 9,
status = "primary",
solidHeader = FALSE,
collapsible = TRUE,
uiOutput("challenges")
)
)
)
)
)
server <- function(input, output,session){
output$operations <- renderUI({
req(input$mySliderText==1)
HTML(markdown::markdownToHTML(knit('trial1.rmd', quiet = TRUE)))
})
}
shinyApp(ui = ui, server = server)
Could you please guide me on how to fix this problem!
The problem is that you are including an full html file within an html page. The conflicts between the two pages is causing the display problem. You need to output an html fragment which excludes the heading. Add fragment.only = TRUE to your markdown render function.
HTML(markdown::markdownToHTML(knit("trial1.rmd", quiet=T),fragment.only = T))
You can also add output: html_fragment in your yaml section inside the rmd file for good measure.
I want the user of my Shiny app to be able to choose between two types of plots by clicking on radiobuttons in the Events panel. The code I have written works, but the page leaves a huge white space when going from "Map" to "Plot". Is there any way to get rid of the white space and position the plot at the very top?
# Load R packages
library(shiny)
library(shinythemes)
library(tidyverse)
library(leaflet)
set.seed(123)
year <- 2001:2020
event <- sample(1:100, size = 20, replace = TRUE)
dat <- as.data.frame(cbind(year, event))
# Define UI
ui <- fluidPage(
shinyjs::useShinyjs(),
theme = shinytheme("journal"),
navbarPage(
"Title",
tabPanel("About",
),
tabPanel("Events",
fluidPage(
titlePanel("Title"),
sliderInput("range", label = "Move slider to select time period", min(2001), max(2020),
value = range(2001:2002), step = 1, sep = "", width = "65%"),
sidebarLayout(
sidebarPanel(
radioButtons("plotType", "Plot type", choices = c("Map" = "m", "Chart" = "l"))),
mainPanel(
leafletOutput("map"),
plotOutput("plot"))
)
)
)
)
)
# Define server function
server <- function(input, output, session) {
observeEvent(input$plotType, {
if(input$plotType == "l"){
shinyjs::disable("range")
}else{
shinyjs::enable("range")
}
})
output$plot <- renderPlot({
if (input$plotType == "l") {
ggplot(dat, aes(year, event)) +
geom_line() +
labs(x = "Year", y = "Events") +
theme_bw()
}
})
output$map <- renderLeaflet({
if ( input$plotType == "m") {
leaflet(dat) %>% addTiles() %>%
fitBounds(~min(11), ~min(54), ~max(67), ~max(24))
}
})
} # server
# Create Shiny object
shinyApp(ui = ui, server = server)
There is a big space because the map html object still exists, but is empty. To avoid this, I created and observeEvent that hides or show the map output depending on input value. I did the same thing with the plot, in cas you need to add others elements below it.
Please note that there are others solutions (conditionalPanel for example), I am just giving you the one I think is the simpliest here.
# Load R packages
library(shiny)
library(shinythemes)
library(tidyverse)
library(leaflet)
set.seed(123)
year <- 2001:2020
event <- sample(1:100, size = 20, replace = TRUE)
dat <- as.data.frame(cbind(year, event))
# Define UI
ui <- fluidPage(
shinyjs::useShinyjs(),
theme = shinytheme("journal"),
navbarPage(
"Title",
tabPanel("About",
),
tabPanel("Events",
fluidPage(
titlePanel("Title"),
sliderInput("range", label = "Move slider to select time period", min(2001), max(2020),
value = range(2001:2002), step = 1, sep = "", width = "65%"),
sidebarLayout(
sidebarPanel(
radioButtons("plotType", "Plot type", choices = c("Map" = "m", "Chart" = "l"))),
mainPanel(
leafletOutput("map"),
plotOutput("plot"))
)
)
)
)
)
# Define server function
server <- function(input, output, session) {
# hide or show map and plot
observeEvent(input$plotType, {
if(input$plotType == "l"){
shinyjs::disable("range")
shinyjs::hide("map")
shinyjs::show("plot")
}
if(input$plotType == "m"){
shinyjs::enable("range")
shinyjs::show("map")
shinyjs::hide("plot")
}
})
output$plot <- renderPlot({
req(input$plotType == "l") # good practice to use req instead of if
ggplot(dat, aes(year, event)) +
geom_line() +
labs(x = "Year", y = "Events") +
theme_bw()
})
output$map <- renderLeaflet({
req(input$plotType == "m")
leaflet(dat) %>% addTiles() %>%
fitBounds(~min(11), ~min(54), ~max(67), ~max(24))
})
} # server
# Create Shiny object
shinyApp(ui = ui, server = server)
So a plotly plot has an embedded rangeslider however I do not like the looks of it. The rangeslider in R Shiny looks much better and professional, however how do i connect the two?
Lets say you have a dataframe with some values and a daterange like:
library(lubridate)
df <- data.frame(
"Date" = c(seq(ymd('2015-09-15'), ymd('2015-09-24'), by = "1 days")),
"values" = c(3,6,5,3,5,6,7,7,4,2)
)
Code for the plotly plot
library(plotly)
plot_df <- plot_ly(df)
plot_df <- plot_df %>% add_lines(type = 'scatter', mode = "lines",
x = ~Date, y = ~values)
Code Shiny
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Basic dashboard"),
dashboardSidebar(),
dashboardBody(
# Boxes need to be put in a row (or column)
fluidRow(
box(plotlyOutput("plotdf", height = 250)),
box(
title = "Controls",
sliderInput("Date", "", min = df$Date[1], tail(df$Date, 1), value = tail(df$Date, 1)
)
)
)
)
)
server <- function(input, output) {
output$plotdf<-renderPlotly({
plot_df
})
}
shinyApp(ui, server)
We can use dplyr::filter and pipe it to plot_ly().
output$plotdf<-renderPlotly({
filter(df, Date <= input$Date) %>%
plot_ly() %>%
add_lines(type = 'scatter', mode = "lines",
x = ~Date, y = ~values)
})
Edit: Below is the plot code separated from the app with a sliderInput to select a range of dates.
library(shiny)
library(dplyr)
library(lubridate)
library(plotly)
source(file = 'my_functions_script.R', local = TRUE)
df <- data.frame(
"Date" = c(seq(ymd('2015-09-15'), ymd('2015-09-24'), by = "1 days")),
"values" = c(3,6,5,3,5,6,7,7,4,2)
)
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Basic dashboard"),
dashboardSidebar(),
dashboardBody(
# Boxes need to be put in a row (or column)
fluidRow(
box(plotlyOutput("plotdf", height = 250)),
box(
title = "Controls",
shiny::sliderInput("Date", "", min = df$Date[1], tail(df$Date, 1), value = c(df$Date[1],tail(df$Date, 1))
)
)
)
)
)
server <- function(input, output) {
output$plotdf<-renderPlotly({
filter(df,Date >= input$Date[[1]], Date <= input$Date[[2]]) %>%
plt()
})
}
shinyApp(ui, server)
I'm trying to click on a category in a pie chart built with highcharts and use the category to filter data in a line chart in R shiny app.
You can capture the click using the hc_plotOptions settings, like so:
library(shiny)
library(highcharter)
ui <- fluidPage(
column(3,
highchartOutput("hcontainer",height = "300px")
),
column(3,
textOutput("clicked")
)
)
server <- function(input, output){
click_js <- JS("function(event) {Shiny.onInputChange('pieclick',event.point.name);}")
output$hcontainer <- renderHighchart({
highchart() %>%
hc_chart(type = "pie") %>%
hc_add_series(data = list(
list(y = 3, name = "cat 1"),
list(y = 4, name = "dog 11"),
list(y = 6, name = "cow 55"))) %>%
hc_plotOptions(series = list(events = list(click = click_js)))
})
output$clicked <- renderText({
input$pieclick
})
}
shinyApp(ui, server)
Following the previous answer from #porkChop you can also add below code to your hc_plotOptions in order to get a selection visualization.
hc_plotOptions(
series = list(
stacking = FALSE, allowPointSelect = TRUE ,events = list(click = click_js))
)
I was wondering if it is possible to save DT table content together with some additional information which is not part of the data frame/table like app version number, date of execution, sliderInput value etc.
Thank you!
Reprex below:
library(shiny)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "range", "Set range", 1, 10, 5, 1)
),
mainPanel(
DT::dataTableOutput("table")
)
)
)
server <- function(input, output) {
dfr <- data.frame(var1 <- c(1,2,3),
var2 <- c(11, 22, 33))
output$table <- DT::renderDataTable(
datatable(dfr, extensions = 'Buttons',
class="cell-border stripe",
rownames = FALSE, colnames = c("var1", "var2"),
options = list(dom = "Blfrtip",
buttond = list("copy", list(extend = "collection",
buttons = c("csv", "excel", "pdf"),
text = "Download")), pageLength=10, autoWidth = TRUE,
searchHighlight = TRUE, filter = "top"))
)
}
shinyApp(ui = ui, server = server)
You could save the contents of the data frame and the other information in a list and then save the list.
Or, any R object can have attributes which are completely arbitrary and under your control. You could set attributes of the data frame to record the information you want.
Personally, I'd use the list approach, purely because I don't like attributes.
Here's a suggestion in response to OP's request below.
library(shiny)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "range", "Set range", 1, 10, 5, 1),
actionButton("saveRds", "Save to Rds"),
actionButton("loadRds", "Load from Rds")
),
mainPanel(
DT::dataTableOutput("table"),
wellPanel(h4("Current data"), verbatimTextOutput("text")),
wellPanel(h4("File data"), verbatimTextOutput("loadedData"))
)
)
)
server <- function(input, output) {
dfr <- data.frame(var1 <- c(1,2,3),
var2 <- c(11, 22, 33))
output$table <- DT::renderDataTable(
datatable(dfr, extensions = 'Buttons',
class="cell-border stripe",
rownames = FALSE, colnames = c("var1", "var2"),
options = list(dom = "Blfrtip",
buttond = list("copy", list(extend = "collection",
buttons = c("csv", "excel", "pdf"),
text = "Download")), pageLength=10, autoWidth = TRUE,
searchHighlight = TRUE, filter = "top"))
)
listInfo <- reactive({
list("data"=dfr, "version"="WebApp Version 1.0", "runDate"=date(), "sliderValue"=input$range)
})
output$text <- renderPrint({
listInfo()
})
observeEvent(input$saveRds, {
saveRDS(listInfo(), "data.Rds")
})
fileData <- reactive({
req(input$loadRds)
readRDS("data.Rds")
})
output$loadedData <- renderPrint({
fileData()
})
}
shinyApp(ui = ui, server = server)
The way you implement "save to file" will depend on the file format: Excel files will clearly have different requirements to PDF files, for example. As a minimum effort demonstation, I've created "Save to Rds" and "Load from RDS" buttons in the sidebar and added a verbatimTextOutput to display the contents of the file when it's loaded. [I'm not sufficiently familiar with DT to know how to add the buttons in the table toolbar.]
OP's effort was pretty close: it's just that writing a list to CSV file takes a little more effort than just calling write.csv...