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

Question: How to use shiny.router with moduleServer? #91

Open
zilch42 opened this issue Dec 17, 2021 · 6 comments
Open

Question: How to use shiny.router with moduleServer? #91

zilch42 opened this issue Dec 17, 2021 · 6 comments

Comments

@zilch42
Copy link

zilch42 commented Dec 17, 2021

hi there,

I have a reasonably large app using shiny.fluent and shiny.router (thanks for these packages they are really great), with a structure similar to the code below:

library(shiny)
library(shiny.fluent)
library(shiny.router)

# navigation pane objects
navigation_styles <- list(
  root = list(height = "100%", width = "30%", boxSizing = "border-box", 
              border = "1px solid #eee", overflowY = "auto"))

link_groups <- list(
  list(
    links = list(
      list(name = 'Home', url = '#!/', key = 'home'),
      list(name = "Documents", key = "documents", url = '#!/documents'),
      list(name = "Pages", key = "pages", url = '#!/pages')
    )))

# page UIs
home_page_ui <- tagList(p("Homepage"))

documents_page_ui <- function(id){
  ns <- NS(id)
  tagList(textOutput(ns("title")))
}

pages_page_ui <- function(id){
  ns <- NS(id)
  tagList(textOutput(ns("title")))
}

# page servers
documents_page_server <- function(id = "documents1"){
  moduleServer( id, function(input, output, session){
    ns <- session$ns
    
    output$title <- renderText("Documents Page")
  })
}

pages_page_server <- function(id = "pages1"){
  moduleServer( id, function(input, output, session){
    ns <- session$ns
    
    output$title <- renderText("Pages Page")
  })
}


# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
  route("/", home_page_ui),
  route("documents", documents_page_ui("documents1")),
  route("pages", pages_page_ui("pages1"))
)


shinyApp(
  ui = fluidPage(
    Nav(
      groups = link_groups,
      selectedKey = "home",
      styles = navigation_styles
    ), 
    fluidPage(
      router$ui
    )
  ),
  
  server = function(input, output, session) {
    router$server(input, output, session)
    
    # page servers
    documents_page_server("documents1")
    pages_page_server("pages1")
  }
)

Each of my pages are inside modules because they are quite similar and it helps me avoid namespace conflicts.
Notice that my page servers are just in the main server function, not in the router.

This works okay, but suffers from the issue of every page reloading when something changes, not just the current page ( #70). From what I understand from reading other issues, this issue has been fixed in the current version of shiny.router, but requires calling the page server from the router - ie. using route(path, ui, server = ACTUAL_SERVER_FUNCTION). Is that correct?

I can only get that to work if the pages are not modules that use moduleServer(). Is there any way to get route to work with modules? And is there any way to pass parameters (e.g. a set of reactive values) to those server modules if it does work?

Cheers

@jakubsob
Copy link

Hello @zilch42, thanks for reaching out!

If I recall correctly there's no documentation on how to use shiny.router with modules, but please take a look at this modification of your example:

library(shiny)
library(shiny.fluent)
library(shiny.router)

# navigation pane objects
navigation_styles <- list(
  root = list(height = "100%", width = "30%", boxSizing = "border-box", 
              border = "1px solid #eee", overflowY = "auto"))

link_groups <- list(
  list(
    links = list(
      list(name = 'Home', url = '#!/', key = 'home'),
      list(name = "Documents", key = "documents", url = '#!/documents'),
      list(name = "Pages", key = "pages", url = '#!/pages')
    )))

# page UIs
home_page_ui <- tagList(p("Homepage"))

documents_page_ui <- function(id){
  ns <- NS(id)
  tagList(textOutput(ns("title")))
}

pages_page_ui <- function(id){
  ns <- NS(id)
  tagList(textOutput(ns("title")))
}

# page servers
documents_page_server <- function(documents_id = "documents1", documents_text = reactive("placeholder")){
  moduleServer(documents_id, function(input, output, session){
    ns <- session$ns
    
    output$title <- renderText(documents_text())
  })
}

pages_page_server <- function(pages_id = "pages1", pages_text = reactive("placeholder")){
  moduleServer(pages_id, function(input, output, session){
    ns <- session$ns
    
    output$title <- renderText(pages_text())
  })
}


# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
  route("/", home_page_ui),
  route("documents", documents_page_ui("documents1"), documents_page_server),
  route("pages", pages_page_ui("pages1"), pages_page_server)
)


shinyApp(
  ui = fluidPage(
    Nav(
      groups = link_groups,
      selectedKey = "home",
      styles = navigation_styles
    ), 
    fluidPage(
      router$ui
    )
  ),
  
  server = function(input, output, session) {
    router$server(
      input,
      output,
      session,
      pages_id = "pages1",
      documents_id = "documents1",
      pages_text = reactive("Pages Page"),
      documents_text = reactive("Documents Page")
    )
  }
)

In router$server function code there's this line:

lapply(routes, function(route) route$server(input, output, 
        session, ...))

which hints us that we can use ellipsis to pass arguments to server functions. We just need to make those arguments names unique, that's why I've used documents_id and pages_id instead of usual id.

Does this example help you solve your issue?

@zilch42
Copy link
Author

zilch42 commented Dec 20, 2021

Thanks @jakubsob. I've applied that method to my app and it works so that's great. I haven't noticed any change in behaviour though. The app still seems to load all pages initially not just the page being viewed, and if I change a reactive value (eg a data filter) that affects other pages they all recalculate at once regardless of which page is currently being viewed. Is that currently still expected behaviour? That's what I was hoping this method would solve

@FMKerckhof
Copy link

It may be a while after the creation of this issue but I would like to second it. Is shiny.router still supported/developed? Appsilon/rhino for example proposes the use of moduleServer and modules as well but doesn't mention shiny.router.

@jakubsob
Copy link

Hi @FMKerckhof, shiny.router is passively maintained now. This means that we don't expect to add any new features soon. shiny.router is a package for routing - you can create an app that uses routing or not depending on your requirements. It's an optional feature and rhino doesn't encourage you nor it disencourages you from using it.

We'll try to reinvestigate this issue and come back with a solution

@mviertel
Copy link

mviertel commented Jul 7, 2022

Hi @jakubsob ,

Thanks for your clarifications especially with respect to feature development.

In the package description you do however mention to make it possible tocustomize loading full session or just visible part in the future. That is a big topic I am struggling with because I usually create dashboards that do have database queries but also use global variables. So, these change the full session and all the queries get refreshed. You guys have a solution for that in your backpack?

Best
Michael

@fernandoroa
Copy link

fernandoroa commented Aug 23, 2022

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

No branches or pull requests

5 participants