As a reminder, the namespace is an important aspect of shiny module that prevents collision of input and output IDs in your application. The namespace identify each unique instance of the modules and need to be provided by the caller.

{tidymodules} can be considered as an object-oriented organisational layer on top of shiny that does conventional module in the background. It does use namespace but make it optional to the users. In other words, you don’t need to remember it!

Optional namespace Id

In the example below a {tm} module MyMod is defined and called with the new() function and with no arguments supplied.
Printing the instance m to the console shows the structure of the module.

library(tidymodules)
#> Loading required package: shiny
#> Loading required package: R6

MyMod <- R6::R6Class(
  "MyMod",
  inherit = TidyModule
)

m <- MyMod$new()

m
#> Module Namespace MyMod-1
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]

The namespace Id for module m above is MyMod-1. It is a unique Id generated by {tidymodules} and composed of the class name and the initialisation order of the module.

User defined namespace Id

The user can also provide a more informative name when initialising a module.

m <- MyMod$new("Tom")

m
#> Module Namespace Tom
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]

Tom end up being the namespace Id of m.

The provided name must begin with a letter [A-Za-z] and may be followed by any number of letters, digits [0-9], hyphens -, underscores _. You should not use the following characters as they have a special meaning on the UI ( CSS / jQuery ).

~ ! @ $ % ^ & * ( ) + = , . / ’ ; : " ? > < [ ]  { } | ` #

Namespace and nested modules

The code below illustrates a very simple example of nested module and the corresponding namespace Ids. Here are few things to note about this example:

  • ModA and ModB are both {tm} classes as they both inherit from tidymodules::TidyModule
  • ModB is used as a nested class of ModA
  • ModA has a public field named nested_mod
  • The namespace of a nested module is always prefixed with its parent
ModB <- R6::R6Class(
  "ModB",
  inherit = TidyModule
)

ModA <- R6::R6Class(
  "ModA",
  inherit = TidyModule,
  public = list(
    nested_mod = NULL,
    initialize = function(...){
      super$initialize(...)
      self$nested_mod <- ModB$new()
    }
  )
)

m <- ModA$new()
n <- m$nested_mod

m
#> Module Namespace ModA-3
#> Module Session global_session
#> - Class ModA << TidyModule << R6
#> - Input [0]
#> - Output [0]
n
#> Module Namespace ModA-3-ModB-4
#> Module Session global_session
#> - Class ModB << TidyModule << R6
#> - Input [0]
#> - Output [0]

Grouping modules

This option allows the grouping of {tm} modules together to facilitate their retrieval from the ModStore and to better visualize them later in a network diagram.

ta <- MyMod$new("Tom",group = "A")
tb <- MyMod$new("Tom",group = "B")

ta
#> Module Namespace A-Tom
#> Module Group A
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]
tb
#> Module Namespace B-Tom
#> Module Group B
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]

As you can see above the group argument is used to define the final namespace Id of the modules. Those modules share the same name but have different namespace IDs.

module’s IDs and UI function

A {tm} module namespace is a concatenation of many fields.

  • Some are optional fields: name and group
  • And some are managed by {tidymodules}: id, parent_ns and module_ns

the module’s namespace is built like this

id = <group>_<name>
module_ns = <parent_ns>_<id>
#' @field name Name of the module, either generated for or provided by the user.
n$name
#> [1] "ModB-4"
ta$name
#> [1] "Tom"

#' @field group Group name of the module.
n$group
#> NULL
ta$group
#> [1] "A"

#' @field id ID of the module. 
n$id
#> [1] "ModB-4"
ta$id
#> [1] "A-Tom"

#' @field parent_ns Parent module namespace in case of nested modules.
n$parent_ns
#> [1] "ModA-3"
ta$parent_ns
#> NULL

#' @field module_ns Module namespace, unique identifier for the module.
n$module_ns
#> [1] "ModA-3-ModB-4"
ta$module_ns
#> [1] "A-Tom"

Like in conventional module, {tm} module also requires wrapping UI elements with the namespace. The function named ns() available in all {tm} modules should be used to namespace the inputs. As you can see in the example below, you simply need to call the function using the self R6 keyword. There is no need to remember the modules’s namespace anymore.

MyMod <- R6::R6Class(
  "MyMod",
  inherit = TidyModule,
  public = list(
    ui = function(){
      shiny::tagList(
        shiny::numericInput(self$ns("inputId"),NULL,0)
      )
    }
  )
)

m <- MyMod$new()
as.character(m$ui())
#> [1] "<div class=\"form-group shiny-input-container\">\n  <label class=\"control-label shiny-label-null\" for=\"MyMod-7-inputId\"></label>\n  <input id=\"MyMod-7-inputId\" type=\"number\" class=\"form-control\" value=\"0\"/>\n</div>"

ModStore and module lookup

The ModStore is an internal repository for all {tm} modules and connections (see communication article for learning how to connect modules). It is a shared environment created by {tidymodules} that orginizes the objects (modules and edges) by applications and sessions. This allows to track and easily retrieve the modules anywhere in the application.

All the examples above show the creation of modules with the new() R6 function and the assignment to variables (pointers to the R6 objects). However {tidymodules} also offers the choice to not save module references and instead use the getMod() or mod() utility functions to retrieve existing module. Note that mod() is just an alias of getMod().

MyMod$new("SaveMeInStore")
#> Module Namespace SaveMeInStore
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]

# look-up by namespace ID
mod("SaveMeInStore")
#> Module Namespace SaveMeInStore
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]

# look-up by index 
mod(2)
#> Module Namespace Tom
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]

# look-up by index within a group
mod(1,group="A")
#> Module Namespace A-Tom
#> Module Group A
#> Module Session global_session
#> - Class MyMod << TidyModule << R6
#> - Input [0]
#> - Output [0]