Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Only load the html for the route #122

Open
1 task done
dawiegriesel opened this issue Mar 17, 2023 · 2 comments
Open
1 task done

[Feature]: Only load the html for the route #122

dawiegriesel opened this issue Mar 17, 2023 · 2 comments

Comments

@dawiegriesel
Copy link

Guidelines

  • I agree to follow this project's Contributing Guidelines.

Description

We have a workaround using UIOutput and renderUI combined with ssession$clientData we can call the module and only transfer the content that is required across to the client based on the url (this is in standard shiny using tabs).

Below I have implemented the same approach / workaround using shiny.router:
`
library(shiny)
library(shiny.router)

This generates menu in user interface with links.

menu <- tags$ul(
tags$li(a(class = "item", href = route_link("/"), "Page")),
tags$li(a(class = "item", href = route_link("second"), "Second page")),
tags$li(a(class = "item", href = route_link("third"), "A third page"))
)

This creates UI for each page.

page <- function(title, content, id) {
ns <- NS(id)
div(
titlePanel(title),
p(content),
textOutput(ns("click_me"))
)
}

Both sample pages.

root_page <- page("Home page", "Converted number of clicks", "root")
second_page <- page("Second page", "Converted number of clicks", "second")
third_page <- page("Third page", "Converted number of clicks", "third")

server_module <- function(id, clicks, power = 1) {
moduleServer(id, function(input, output, session) {
output$click_me <- renderText({
as.numeric(clicks())^power
})
})
}

Create output for our router in main UI of Shiny app.

ui <- fluidPage(
menu,
actionButton("clicks", "Click me!"),
router_ui(
route("/", uiOutput("root_out")),
route("second", uiOutput("second_out")),
route("third", uiOutput("third_out"))
)
)

Plug router into Shiny server.

server <- function(input, output, session) {

router_server()

clicks <- reactive({
input$clicks
})

observe({
if (session$clientData$url_hash == "#!/") {
output$root_out <- renderUI(root_page)
server_module("second", clicks = clicks, power = 2)
}
if (session$clientData$url_hash == "#!/second") {
output$ui <- renderUI(second_page)
server_module("second", clicks = clicks, power = 2)
}
if (session$clientData$url_hash == "#!/third") {
output$ui <- renderUI(third_page)
server_module("second", clicks = clicks, power = 2)
}
})

server_module("root", clicks = clicks)
server_module("third", clicks = clicks, power = 3)
}

Run server in a standard way.

shinyApp(ui, server)
`

From the screenshot you can see that only the root_page content was transferred to the client.

image

This is really useful when you are dealing with large shiny apps that cater for different users and scale well because sessions only deal with the content that they require and not the full app. In certain instances the user rights restrict them to not use certain parts of the app but with the current way shiny.router works, they will still sacrifice load time based on the app and not the session.

Open to discussions about this if you are interested.

Problem

NO

Proposed Solution

Only transfer html for the current route.

Alternatives Considered

Brochure is working in the desired way but only works with Golem. We would like to migrate our app to Rhino but this is preventing us from doing that.

@jmaagdenbergSB
Copy link

I have an issue with deploying a shiny app on a domain that is protected by a load balancer.
I read that this might be because shiny.router is hash based rather than path based.

Will this solve that issue or is there something else that i should consider?

@krystian8207
Copy link
Contributor

@jmaagdenbergSB Unfortunately not, the solution would still be hash-based.

@dawiegriesel The approach you've taken was used previously in shiny.router but was changed to the current one with version 0.2.0 (see #75).
From the performance perspective indeed we could spare resources by rendering active route page only, on the other hand the page needs to be rerendered from scratch every time we open it which is also time and resources consuming (along with a few other issues which were solved by the new approach, see attached PR for more details).
If you want to prevent rendering inactive pages you could easily define css:

.router-hidden {
  display: none;
}

Shiny by default prevents from rendering elements that have display: none css property set, so I think this is the best approach to use in your case.

Krystian

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants