8

It's possible to use anchor links in flat Shiny apps relatively easily - https://stackoverflow.com/a/28605517/1659890.

However, is it possible for an external link to target a specific tabPanel of a navbarPage in a Shiny app?

Consider the following test app: UI:

shinyUI(navbarPage(
 "Anchor Test",
 tabPanel(
 "Panel 1", fluidPage(HTML(
 paste(
 "<p>I'm the first panel</p>","<p><a href='#irisPlotUI'>Link to irisPlotUI in panel 2</a></p>",
 "<a href='#panel2'>Link to panel 2</a>",sep = ""
 )
 ))
 ),
 tabPanel("Panel 2", fluidPage(
 "I'm the second panel", uiOutput("irisPlotUI")
 ),
 value = "#panel2"),
 tabPanel("Panel 3", "I'm a table!!!")

Server:

shinyServer(function(input, output) {
 output$irisPlotUI <- renderUI(plotOutput("irisPlot"))
 output$irisPlot <- renderPlot(plot(iris$Sepal.Length))
 })

Using the method from the linked answer doesn't work, the id of irisPlotUI is correct but is a child of the tabPanel that the plot lives in.

The data-value of the tabPanel is given the value of "panel2" through the use of the argument value, however the Shiny app continues to give the tabPanel a unique id that I don't know how to target.

I've examined the source of deployed Shiny apps, for instance https://martinjhnhadley.shinyapps.io/AnchorLinks, and found the actual link to the tabPanel: https://internal.shinyapps.io/martinjhnhadley/AnchorLinks/?initialWidth=1074&childId=shinyapp#tab-8850-2

However, navigating directly to this link does not direct me to the tab either.

Are anchor links my only option in targeting parts of the app or is there a Shiny specific solution?

If not, how might I insert a script such as shown here https://stackoverflow.com/a/15637801/1659890 to allow javascript to be executed when landing on the Shiny app to select the tabPanel in a similar manner to this https://groups.google.com/d/msg/shiny-discuss/sJlasQf71fY/RW7Xc8F02IoJ

Solution thanks to daattali

Using daattali's answer I was able to find the following also from him - https://github.com/rstudio/shiny/issues/772#issuecomment-112919149

The following achieves exactly my needs, note that I have chosen to keep with the /?url= convention he used:

UI.R

shinyUI(
 navbarPage( "test", id = 'someID',
 tabPanel("tab1", h1("page1")),
 navbarMenu( "menu",
 tabPanel('tab2a', value='nested1', h1("page2a")),
 tabPanel('tab2b', value='nested2', h1("page2b")),
 tabPanel('tab_sub', "foobar")
 ),
 tabPanel("tab3", h1("page3"))
 ))

SERVER.R

url1 <- url2 <- ""
shinyServer(function(input, output, session) {
 values <- reactiveValues(myurl = c(), parent_tab = "")
 observe({
 # make sure this is called on pageload (to look at the query string)
 # and whenever any tab is successfully changed.
 # If you want to stop running this code after the initial load was
 # successful so that further manual tab changes don't run this,
 # maybe just have some boolean flag for that.
 input$someID
 input$tab_sub_tabs
 query <- parseQueryString(session$clientData$url_search)
 url <- query$url
 if (is.null(url)) {
 url <- ""
 }
 # "depth" is how many levels the url in the query string is
 depth <- function(x) length(unlist(strsplit(x,"/")))
 # if we reached the end, done!
 if (length(values$myurl) == depth(url)) {
 return()
 }
 # base case - need to tell it what the first main nav name is
 else if (length(values$myurl) == 0) {
 values$parent_tab <- "someID"
 }
 # if we're waiting for a tab switch but the UI hasn't updated yet
 else if (is.null(input[[values$parent_tab]])) {
 return()
 }
 # same - waiting for a tab switch
 else if (tail(values$myurl, 1) != input[[values$parent_tab]]) {
 return()
 }
 # the UI is on the tab that we last switched to, and there are more
 # tabs to switch inside the current tab
 # make sure the tabs follow the naming scheme
 else {
 values$parent_tab <- paste0(tail(values$myurl, 1), "_tabs")
 }
 # figure out the id/value of the next tab
 new_tab <- unlist(strsplit(url, "/"))[length(values$myurl)+1]
 # easy peasy.
 updateTabsetPanel(session, values$parent_tab, new_tab)
 values$myurl <- c(values$myurl, new_tab)
 })
})
asked Oct 8, 2015 at 16:56
3
  • I forgot about that code I wrote :) I think this code is overly complex for your needs, unless you have many tabs and each tab can have nested tabs and you don't know ahead of time the names of the tabs. That solution that I wrote was specifically for such a general case. In your case, if you only have one level of tabs (vs nested tabs) then most of that code is unnecessary Commented Oct 9, 2015 at 1:26
  • @daattali ahh, well that's certainly true for this minimal working example but the real shiny app has many levels. Good violent though. Personally I think this is something others would benefit from and was hard to find, blog worthy? Commented Oct 9, 2015 at 4:44
  • 1
    I'm planning on having a blog pots soon about many different intermediate to advanced shiny techniques, which will include many common problems I see often and the snippets of code to solve them along with explanations. I already have this one in there :) Commented Oct 9, 2015 at 7:17

2 Answers 2

7

I found the other answer a little difficult to follow so I created the example below. To navigate to the Data Availability tab, place "?a=b" to at the end of your url. For example if hosting the app on port 7436 the following link will take you directly to the Data Availability page. http://127.0.0.1:7436/?a=b

library(shinydashboard)
library(shiny)
ui <- dashboardPage(
 dashboardHeader(),
 dashboardSidebar(
 sidebarMenu(id = "container",
 menuItem("Channels", tabName = "lt_channels"),
 menuItem("Data Availability", tabName = "data_avail")
 )),
 dashboardBody(
 tabItems(
 tabItem(tabName = "lt_channels",
 h1("Test Fail")),
 tabItem(tabName = "data_avail",
 h1("Test Pass"))
 )
 )
)
server <- function(session, input, output){
 observe({
 query <- parseQueryString(session$clientData$url_search)
 query1 <- paste(names(query), query, sep = "=", collapse=", ")
 print(query1)
 if(query1 == "a=b"){
 updateTabItems(session, inputId = "container", selected = "data_avail")
 }
 })
}
shinyApp(ui = ui, server = server)
answered Dec 5, 2017 at 14:06
Sign up to request clarification or add additional context in comments.

Comments

4

You could add a search query parameter to the URL (eg. href='www.myapp.com?tab=tab2), and in the app that is being linked you would have to add a bit of logic that changes to a specified tab on initialization if the search string is present (eg. look at session$clientData$url_search and if it exists and there's a tab variable, then use updateTabsetPanel() to go to that tab)

answered Oct 8, 2015 at 20:05

1 Comment

Many thanks for this - turns out I've been referring to a lot of content before now, good to be able to say thanks :)

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.