8

Consider any Shiny module in which we use session$ns in the server part, e.g. the one below.

We could equivalently use NS(id) instead of session$ns. So why it is recommended to use session$ns? Is there an advantage over NS(id)?

library(shiny)

myModuleUI <- function(id){
  ns <- NS(id)
  uiOutput(ns("checkbox"))
}

myModuleServer <- function(id){
  moduleServer(id, function(input, output, session){
    ns <- session$ns
    output[["checkbox"]] <- renderUI({
      checkboxInput(ns("checkbox"), label = "Check me", value = FALSE)
    })
    observe({
      print(input[["checkbox"]])
    })
  })
}

ui <- basicPage(
  myModuleUI("myModule")
)

server <- function(input, output, session){
  myModuleServer("myModule")
}

shinyApp(ui, server)
Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • 1
    'recommended to use session$ns' - please enlighten me. – Kitswas Aug 13 '22 at 14:05
  • Probably because it is mentioned in the official [shiny tutorial](https://shiny.rstudio.com/articles/modules.html), which e.g. I also used when I created a module tutorial – starja Aug 13 '22 at 14:17

2 Answers2

5

When I see it correctly, it's due to how the module API was structured before shiny 1.5.0. Until then, you had to write module server functions in the following way:

myModuleServer <- function(input, output, session){
  ns <- session$ns
  output[["checkbox"]] <- renderUI({
    checkboxInput(ns("checkbox"), label = "Check me", value = FALSE)
  })
  observe({
    print(input[["checkbox"]])
  })
}

and call a module with:

callModule(myModuleServer, id = "myModule")

With this API, you don't have an id variable for your module server function and you had to resort to session$ns. This is still (erroneously) mentioned in the "Using renderUI within modules" part of the shiny tutorial.

If your renderUI block itself contains inputs/outputs, you need to use ns() to wrap your ID arguments, just like in the examples above. But those ns instances were created using NS(id), and in this case, there’s no id parameter to use. What to do? The session parameter can provide the ns for you; just call ns <- session$ns. This will put the ID in the same namespace as the session.

The above is not true any more for the new API.

Indeed, in Mastering Shiny, they use NS(id) also in the server part of a module.

However, I'm not sure if there are any subtle differences between using NS(id) and session$ns in the module server with the new API (but considering "Mastering Shiny" uses it, I don't think so).

starja
  • 9,887
  • 1
  • 13
  • 28
1

Why?

session$ns is mandatory if you want to use nested modules which use dynamic UI with uiOutput

Minimal example

## NESTED MODULE
nestedModuleUI <- function(id) {
  ns <- NS(id)
  shiny::uiOutput(ns("ui_out"))
}

nestedModuleServer <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {

      output$ui_out <- shiny::renderUI({
        list(
          shiny::actionButton(session$ns("well_formed"), "I work"),
          shiny::actionButton(NS(id, "bad_formed"), "I do nothin'")
        )
      })

      observeEvent(input$well_formed,  print("button pressed!"))
      observeEvent(input$bad_formed,  print("button pressed!"))
    }
  )
}

## CONTAINER MODULE
containerModuleUI <- function(id) {
  ns <- NS(id)
  nestedModuleUI(ns("nested"))
}

containerModuleServer <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {
      nestedModuleServer("nested")
    }
  )
}

## Application
library(shiny)
ui <-  containerModuleUI("container")

server <- function(input, output, session) containerModuleServer("container")

shinyApp(ui, server)

see also this answer

pietrodito
  • 1,783
  • 15
  • 24