Usage of Modules and stratégie du petit r with navbarPage

I try to understand the usage of modules and stratégie du petit r.
For this, I built a small application.
A navBarpage with 2 panels. For each tabPanel one module exists which creates the UI and contains the server logic for the panel.
tabPanel 1 / Module something

  • numeric Input -> user can choose between 1,2,3
  • action button. When pressed the panel View changes.

tabPanel 2 / Module happend

  • shows the chosen number

I would like to receive feedback if I used the components correctly?
Also I'm interested: Does it make sense to structure an application in the way that each tabPanel is 1 Module? For Example File Upload, Overview, Clustering, and Reporting.

  1. app_ui (UI-structure and calls module UI)
  2. server_ui (calls Module Servers)
  3. mod_something (Numberinput and actionButton to change panel)
  4. mod_happend ( Displays number)

app_ui

app_ui <- function(request) {
  tagList(
    # Leave this function for adding external resources
    golem_add_external_resources(),
    # Your application UI logic
    fluidPage(
      navbarPage("navBarPage",
                 id = "navBarID",
                 tabPanel("title1",
                          mod_something_ui("something_1")),
                 tabPanel("title2",
                          mod_happend_ui("happend_1")),
      )
    )
  )
}

app_server

app_server <- function(input, output, session) {
  r <- reactiveValues()
  mod_something_server("something_1", r = r,parent = session)
  mod_happend_server("happend_1",r)

}

mod_something

tmod_something_ui <- function(id){
  ns <- NS(id)
  tagList(
    selectInput(inputId = ns("numberInput"), label = "choose", choices = c(1,2,3)),
    actionButton(inputId = ns("doSomething"),label = "Change Page!"),

  )
}

mod_something_server <- function(id,r,parent){
  moduleServer(id, function(input, output, session){
    ns <- session$ns
    observe({
      r$numberInput <- input$numberInput
    })
    observeEvent(eventExpr = input$doSomething,
                 updateNavbarPage(session = parent, inputId = "navBarID", selected = "title2"))

  })
}

mod_happend

mod_happend_ui <- function(id){
  ns <- NS(id)
  tagList(
  p("the Number is"),
  textOutput(ns("choosenNumber")),
  )
}

mod_happend_server <- function(id,r){
  moduleServer( id, function(input, output, session){
    ns <- session$ns
    number <- reactive(r$numberInput)
    output$choosenNumber <- renderText(number())

  })
}

Hey @leosprengel

So, to start with your second question, yes, I would advise going for one module (and possibly submodule) by tab so yes, your pattern is correct (as far as I view a correct shiny infra :slight_smile: ).

tabPanel("title1", mod_something_ui("something_1")),
tabPanel("title2",mod_happend_ui("happend_1"))

For your first question about the r, yes, this is the way to do it.
You can think of the r as a "small database" where you store the values, and that goes from the top and flows inside the modules and submodule. As this is just one object, values are updated and reactivity happens accordingly.

app_server <- function(input, output, session) {
  r <- reactiveValues() # Creating the r object
  mod1("one", r = r) # passing "down" the object to the modules
  mod2("two",  r = r)
}

Just another feedback about your code, I think this pattern is a bit overkill:

    number <- reactive(r$numberInput)
    output$choosenNumber <- renderText(number())

Given that r is a reactive list, renderText(r$numberInput) is enough.

Here is the full reprex :

library(shiny)

mod_something_ui <- function(id) {
    ns <- NS(id)
    tagList(
        selectInput(inputId = ns("numberInput"), label = "choose", choices = c(1, 2, 3))
    )
}

mod_something_server <- function(id, r, parent) {
    moduleServer(id, function(input, output, session) {
        ns <- session$ns
        observe({
            r$numberInput <- input$numberInput
        })
    })
}

mod_happend_ui <- function(id) {
    ns <- NS(id)
    tagList(
        p("the Number is"),
        textOutput(ns("choosenNumber")),
    )
}

mod_happend_server <- function(id, r) {
    moduleServer(id, function(input, output, session) {
        ns <- session$ns
        output$choosenNumber <- renderText(r$numberInput)
    })
}

ui <- fluidPage(
	mod_something_ui("something_1"),
	mod_happend_ui("happend_1")
)

server <- function(input, output, session) {
  r <- reactiveValues()
  mod_something_server("something_1", r = r)
  mod_happend_server("happend_1", r = r)
}

shinyApp(ui, server)

Hope this helps,
Colin

1 Like

Much appreciated! Thanks for your input!

Regarding the overkill use of reactive for values stored in r
If I understand you correctly because the inputs are already reactive there is no reason to wrap them in a reactive?

Also thank you very much for your support for the book engineering shiny!
I started in September to dig through mastering Shiny and after through engineering shiny and enjoyed the Examples and Theoretical input! Also, the recommendation to use Golem right from the beginning has helped me tremendously to structure my application :slight_smile:

1 Like

Yep, your r is already reactive, so there is no need to wrap it in reactive() :slight_smile:

Thanks a lot for your nice words, very happy to hear you enjoy the book and the tools!

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.