shiny - what page is the user looking at? - r

How can I determine if the user is looking at a particular page in a flexdashboard? I have a global sidebar panel of filters that applies to all pages except one of the filters in the sidebar doesn't apply to one of the pages. In this example, let's say I don't want the selectInput() to be displayed on Page 3.
---
title: "Which Page?"
runtime: shiny
output: flexdashboard::flex_dashboard
---
Sidebar {.sidebar data-width=400}
=====================================
```{r}
textInput("text", NULL)
selectInput("select", NULL, LETTERS[1:5])
```
Page 1
=====================================
Page 2
=====================================
Page 3
=====================================
I was hoping to use session$clientData$url_hash but this is static and based on the page the user initially opened. You can put this in the sidebar section to see how it (doesn't) change. Other ideas I've found are to use window.location or is.visible in js but not sure how to integrate.
print_info <- function(x) {
allvalues <-
lapply(
X = sort(names(x)),
FUN = function(name) {
item <- x[[name]]
paste(name, item, sep = " = ")
}
)
paste(allvalues, collapse = "\n")
}
output$client_data <- renderText(print_info(session$clientData))
verbatimTextOutput("client_data")

Solution 1 : "pure Shiny"
You can take advantage of the fact that renderXXX() is only calculated on active page. For example here we can use renderUI() on each page to update a reactive value tab with the current page number. That value is then used in the side bar to check if the selectInput will be displayed with the renderUI :
---
title: "Which Page?"
runtime: shiny
output: flexdashboard::flex_dashboard
---
Sidebar {.sidebar data-width=400}
=====================================
```{r}
textInput("text", NULL)
tab <- reactiveVal(1)
renderUI({
if(tab()!=3)
selectInput("select", NULL, LETTERS[1:5])
})
```
Page 1
=====================================
```{r}
renderUI({tab();tab(1);NULL})
```
Page 2
=====================================
```{r}
renderUI({tab();tab(2);NULL})
```
Page 3
=====================================
```{r}
renderUI({tab();tab(3);NULL})
```
Solution 2 : with JavaScript
You can also use a JS event
(note the use of is.null() as event is not triggered on page 1 initial display):
---
title: "Which Page?"
runtime: shiny
output: flexdashboard::flex_dashboard
---
<script>
$("body").on("shown.bs.tab", "a[data-toggle='tab']", function(e) {
Shiny.setInputValue("active_tab", $(e.target).parent().index() + 1);
})
</script>
Sidebar {.sidebar data-width=400}
=====================================
```{r}
textInput("text", NULL)
renderUI({
if(is.null(input$active_tab) || input$active_tab!=3)
selectInput("select", NULL, LETTERS[1:5])
})
```
Page 1
=====================================
Page 2
=====================================
Page 3
=====================================

Related

Flexdashboard: set anchors for ### headings (level3)

In flexdashbard, we can create different headings:
Heading level 1: they are pages
Heading level 2: we specify Row or Column
Heading level 3: tabs
Now for anchors, it seems that we can only set them for heading level 1. As we can see it in this article.
My question is, it is possible to create anchors for tabs (so heading level 3)?
I tried to find a solution. For example, with the following code:
Page 4
=====================================
## Row {.tabset}
### tab 1 {#test1}
### tab 2 {#test2}
The anchor is automatically created for "Page 4" which is #page-4. For "tab 1", I tried to add {#test1}, but it doesn't work.
EDIT: solution with javascript
Another solution that would work for me is to use javascript, to go the the next tab.
First we can add a javascript
<script type="text/javascript">
$('.btnNext').click(function(){
$('.nav-tabs > .active').next('li').find('a').trigger('click');
});
$('.btnPrevious').click(function(){
$('.nav-tabs > .active').prev('li').find('a').trigger('click');
});
</script>
Then, we can create buttons to navigate
<a class="btn btn-primary btnNext">Next</a>
<a class="btn btn-primary btnPrevious">Previous</a>
But I testd in R Markdown, it doesn't work.
update
I now figured out a way to go from #page-4 #test1 to #page-5 #test4 by clicking a link. I use a bit of javascript to read the URLs parameters. This allows us to define links like a(href="?page5&tab=4"). The javascript will get the parameters, in our case page as 5 and tab as 4 and then execute two clicks, one to get to #page-5 and another one to get the tab 4 called #test4. There are probably better options which allow you to set the active page tab and tabset tab, but I didn't get them working with {flexdashboard}. Anyway, I hope the approach below solves your problem.
---
title: "Untitled"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
library(htmltools)
```
```{js}
document.addEventListener("DOMContentLoaded", function(){
var url_string = window.location.href;
var url = new URL(url_string);
var page = url.searchParams.get('page');
var pageid = '#page-'.concat(url.searchParams.get('page'));
var tab = 'tab'.concat(url.searchParams.get('tab'));
var tabid = '#test'.concat(url.searchParams.get('tab'));
$('a[href="'+ pageid +'"]').click();
$('a[href="'+ tabid +'"]').click();
});
```
Page 4
=====================================
## Row {.tabset}
### tab 1 {#test1}
```{r}
tags$a(href = "?page=5&tab=4",
shiny::actionButton("btn1",
"go to page-5 tab4"
))
```
### tab 2 {#test2}
```{r}
tags$a(href = "#test1",
shiny::actionButton("btn4",
"go to tab1"
))
```
Page 5
=====================================
## Row {.tabset}
### tab 3 {#test3}
```{r}
tags$a(href = "#test4",
shiny::actionButton("btn5",
"go to tab4"
))
```
### tab 4 {#test4}
```{r}
tags$a(href = "?page=4&tab=2",
shiny::actionButton("btn6",
"go to page-4 tab2"
))
```
old answer
In my case your header level 3 anchors ({#test1} etc.) are working even when not using runtime: shiny. You can change the tabs via action buttons, but only if you are on the same page. For example you can from tab1 to tab2 on page 4 but you cannot go from tab1 on page 4 to tab4 on page 5. But changing from page 4 to page 5 is again possible.
---
title: "Untitled"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
library(htmltools)
```
Page 4
=====================================
## Row {.tabset}
### tab 1 {#test1}
```{r}
tags$a(href = "#test2",
shiny::actionButton("btn1",
"go to tab2"
))
tags$a(href = "#test1",
shiny::actionButton("btn2",
"go to tab3 (not working)"
))
tags$a(href = "#page-5",
shiny::actionButton("btn3",
"go to page5 (working)"
))
```
### tab 2 {#test2}
```{r}
tags$a(href = "#test1",
shiny::actionButton("btn4",
"go to tab1"
))
```
Page 5
=====================================
## Row {.tabset}
### tab 3 {#test3}
```{r}
tags$a(href = "#test4",
shiny::actionButton("btn5",
"go to tab4"
))
```
### tab 4 {#test4}
```{r}
tags$a(href = "#test3",
shiny::actionButton("btn6",
"go to tab3"
))
```

Modal popup for valueBox with shinyjs and flexdashboard

I'm trying to trigger a popup message for a valueBox in flexdashboard using shinyjs. There's a similar question here but it doesn't work with flexdashboard.
The flexdashboard is created using an RMarkdown file.
---
title: "Test valuebox"
output:
flexdashboard::flex_dashboard:
orientation: rows
runtime: shiny
---
```{r}
library(flexdashboard)
library(shiny)
library(shinyjs)
useShinyjs(rmd = TRUE)
onclick('valbox', showModal(modalDialog(
title = "message",
"This is an important message!"
)))
```
Inputs {.sidebar}
-------------------------------------
```{r ui}
sliderInput('valsel', 'Select number:', min = 0, max = 10, value = 5)
```
Column
-------------------------------------
###
```{r}
output$valbox <- renderValueBox(flexdashboard::valueBox(input$valsel, "selected"))
valueBoxOutput('valbox')
```
I've inspected the HTML created by the app and the id for the value box is indeed 'valbox'. I tried creating a unique id with tags, but that didn't work either.

Select multiple items for search keyword by pressing Enter in R Shiny input

I am trying to select items in Shiny's select input by using entered search keyword and pressing Enter to select all matching items for keyword.
The observe function in the snippet works if I provide an item like ALL that is already present in the list but I want it to work for any typed keyword. e.g. App and hit Enter to select all matching items.
It will be interesting to see if there are other custom options that can be coded using jquery or something else to capture the typed input and capture filtered items. Or may be some regex instead of the "ALL" that I used in the if condition.
---
title: "search and select multiple items by pressing Enter"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
```
Column {.sidebar data-width=300}
-----------------------------------------------------------------------
```{r}
#####################
### Reactive Parameters
Parameters <- reactive({
c("ALL","Apple","App","Application","Approximate","Appointment","Ap_titude","Apricot","B","Ball","Bat","Battery")
})
output$params = renderUI({
selectInput(
'params',
'Parameters',
choices = Parameters(),
multiple = TRUE,
selectize = TRUE
)
})
observe({
if("ALL" %in% input$params){
param_selection <- setdiff(Parameters(), "ALL")
} else {
param_selection <- input$params
}
updateSelectInput(session, "params", selected = as.character(unlist(param_selection)))
})
uiOutput("params")
```
Column
-----------------------------------------------------------------------
### Summary
```{r}
```
I found help for selectize.js . It was hyperlinked on selectize page of Shiny.
I ended up using the create function to get it to work. Had to use callback instead of return. The selection based on search string was showing undefined, I could not get it to show correct selection. But since I had the observe function through which I was going to updateSelectInput, I did not worry about that.
Here's a sample code that I put together.
---
title: "search and select multiple items by pressing Enter"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(dplyr)
```
Column {.sidebar data-width=300}
-----------------------------------------------------------------------
```{r echo=FALSE}
#####################
### Reactive Parameters
Parameters <- reactive({
c("ALL","Apple","App","Application","Approximate","Appointment","Ap_titude","Apricot","B","Ball","Bat","Battery")
})
output$params = renderUI({
selectizeInput(
'params',
'Parameters',
selected = NULL,
choices = Parameters(),
multiple = TRUE,
options = list(
delimiter= ',',
persist= FALSE,
create = I("function(input, callback) {
callback({
'value': input,
'text': input
});
}")
)
)
})
observe({
dt <- as.character(unlist(Parameters()))
if(is.null(input$params)){
return()
} else{
if("ALL" %in% input$params){
param_selection <- setdiff(dt, "ALL")
} else {
param_selection <- dt[grep(paste(input$params, collapse = "|"), dt)]
}
}
updateSelectInput(session, "params", selected = as.character(unlist(param_selection)))
})
uiOutput("params")
```
Column
-----------------------------------------------------------------------
### Summary
```{r}
```
And this is the output:
Search string- "App", add it
The moment you click, "Add App", observe function triggers and updates the selection to all the values that match the keyword.
Hope this helps someone else that faces the same issue like I did.

How to fix download button sidebar issue in flexdashboard

I have added a download button to my flexdashboard in the sidebar panel, but it appears in the main panel when I knit the .RMD. Can you please guide me as to how I can fix it?
Here's a minimal example of what I'm trying to accomplish
---
title: "Download Button in Wrong Panel"
output:
flexdashboard::flex_dashboard:
vertical_layout: scroll
runtime: shiny
---
```{r setup, include=FALSE}
## Setting up required libraries
library(flexdashboard)
library(dplyr)
library(shiny)
library(knitr)
dataset <- read.csv(somefile)
```
Inputs {.sidebar}
-----------------------------------------------------------------------
### Input Filters
```{r input}
## Metric 1
selectInput('metric',
'Choose Metric',
names(dataset %>% select(-default_column)),
selected = "default_metric")
## Download Button
downloadButton('downloadData','Download Result Set')
```
Outputs
-----------------------------------------------------------------------
### List of Customers
```{r output}
subset_dataset <- reactive({
dataset[,c("default_column",input$metric)]
})
renderTable({
subset_dataset()
},
include.rownames = FALSE)
downloadHandler(filename = function() {
paste('resultset-', Sys.Date(), '.csv', sep='')
},
content = function(file) {
write.csv(subset_dataset(), file, row.names = FALSE)
}
)
```
A screenshot of the dashboard is as follows
Thanks!
Never mind, I fixed it and it was rather silly of me to have not tried it before posting the question, but if someone ever faces a similar problem, the solution is here.
The download handler function must simply be placed in the sidebar panel as well and that does it.
Inputs {.sidebar}
-----------------------------------------------------------------------
### Input Filters
```{r input}
## Metric 1
selectInput('metric',
'Choose Metric',
names(dataset %>% select(-default_column)),
selected = "default_metric")
## Download Button
downloadButton('downloadData','Download Result Set')
downloadHandler(filename = function() {
paste('resultset-', Sys.Date(), '.csv', sep='')
},
content = function(file) {
write.csv(subset_dataset(), file, row.names = FALSE)
}
)

Conditional reactive logic shiny based flexdashboard

I am trying to contiditonally do either one type of render (renderPlot) or another (renderText) based on some input. Here's what I tried:
---
title: "Citation Extraction"
output:
flexdashboard::flex_dashboard:
vertical_layout: scroll
orientation: rows
social: menu
source_code: embed
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
```
Sidebar {.sidebar}
=====================================
```{r}
textInput("txt", "What's up?:")
```
Page 1
=====================================
### Chart A
```{r}
urtxt <- reactive({input$txt})
if (nchar(urtxt()) > 20){
renderPlot({plot(1:10, 1:10)})
} else {
renderPrint({
urtxt()
})
}
```
But it states:
So I tried adding a reactive around the conditional resulting in returning the function reactive returns.
reactive({
if (nchar(urtxt()) > 20){
renderPlot({plot(1:10, 1:10)})
} else {
renderPrint({
urtxt()
})
}
})
How can I have conditional reactive logic?
To get different kind of output depending on the length of the inputed character string you can do following:
1) Create a dynamic output uiOutput,
2) In the reactive environment renderUI, depending on the input, choose kind of the output.
3) Render the output
---
title: "Citation Extraction"
output:
flexdashboard::flex_dashboard:
vertical_layout: scroll
orientation: rows
social: menu
source_code: embed
runtime: shiny
---
```{r setup, include=FALSE}
library(flexdashboard)
library(shiny)
```
Sidebar {.sidebar}
=====================================
```{r, echo = F}
textInput("txt", "What's up?:", value = "")
```
Page 1
=====================================
### Chart A
```{r, echo = F}
uiOutput("dynamic")
output$dynamic <- renderUI({
if (nchar(input$txt) > 20) plotOutput("plot")
else textOutput("text")
})
output$plot <- renderPlot({ plot(1:10, 1:10) })
output$text <- renderText({ input$txt })
```

Resources