BristolMyersSquibb / blockr

Composable, extensible no-code UI
https://bristolmyerssquibb.github.io/blockr/
GNU General Public License v3.0
34 stars 3 forks source link

Stacks within modules #425

Open JohnCoene opened 1 day ago

JohnCoene commented 1 day ago

We define stack names when creating the stack.

library(blockr)
library(shiny)

mod_ui <- function(id) {
  uiOutput(shiny::NS(id)("stack"))
}

mod_server <- function(id) {
  moduleServer(
    id,
    function(
      input,
      output,
      session
    ) {
      # "hello" or session$ns("hello") both fail
      stack <- new_stack(name = "hello")
      output$stack <- renderUI({
        generate_ui(stack)
      })
      generate_server(stack)
    }
  )
}

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  mod_ui("stack")
)

server <- function(input, output, session) {
  mod_server("stack")
}

shinyApp(ui, server)

The hack is to manually change the name attribute

stack <- new_stack(name = "hello")
generate_server(stack)
attr(stack, "name") <- session$ns("hello")
output$stack <- renderUI({
  generate_ui(stack)
})

EDIT: In the first example, the stack renders fine but underlying namespace is incorrect, none of the inputs of the stack work.

DivadNojnarg commented 1 day ago

We have an example in the doc, where you can pass custom id to generate_ui and generate_server.

library(blockr)
library(shiny)

mod_ui <- function(id) {
  uiOutput(shiny::NS(id)("stack"))
}

mod_server <- function(id) {
  moduleServer(
    id,
    function(
      input,
      output,
      session
    ) {
      # "hello" or session$ns("hello") both fail
      stack <- new_stack()
      output$stack <- renderUI({
        generate_ui(stack, id = session$ns("hello"))
      })
      generate_server(stack, id = "hello")
    }
  )
}

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  mod_ui("stack")
)

server <- function(input, output, session) {
  mod_server("stack")
}

shinyApp(ui, server)

If have to check why passing a name to stack fails in that case.

DivadNojnarg commented 1 day ago

When we call new_stack(name = session$ns("pouet")), we need the session since this is used on the UI side. On the server side, we don't need the namespace. This means there is an issue in the generate_server.stack function where we extract the id:

id <- coal(id, get_stack_name(x))

as get_stack_name(x) could contain the parent module namespace, which we don't want.

What we need is something like this in generate_server.stack, where we only remove the outer namespace:

id <- coal(id, tail(strsplit(get_stack_name(x), "-")[[1]], n = 1))

Example with one more level of parent module:

library(blockr)
library(shiny)

mod_inner_ui <- function(id) {
  uiOutput(shiny::NS(id)("stack"))
}

mod_inner_server <- function(id) {
  moduleServer(
    id,
    function(
      input,
      output,
      session
    ) {
      stack <- new_stack(name = session$ns("pouet"))
      output$stack <- renderUI({
        generate_ui(stack)
      })
      generate_server(stack)
    }
  )
}

mod_outer_ui <- function(id) {
  ns <- NS(id)
   mod_inner_ui(ns("inner"))
}

mod_outer_server <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {
      mod_inner_server("inner")
    }
  )
}

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  mod_outer_ui("ext")
)

server <- function(input, output, session) {
  mod_outer_server("ext")
}

shinyApp(ui, server)