Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using lapply() in renderUI() in Shiny Module

I am trying to convert a section of code into a Shiny Module, but my renderPlot() functions generated within an lapply() don't seem to be working. I have created a simple example below to demonstrate the issue.

(Note: Here I'm using renderText() calls, but the same behavior applies.)

app_normal.R:

library(shiny)

ui <- fixedPage(
  h2("Normal example"),
  uiOutput("test")
)

server <- function(input, output, session) {
  output$test <- renderUI({
    lapply(1:3, function(val) {
      fluidRow(column(12,renderText(paste("Line", val))))
    })
  })
}

shinyApp(ui, server)

app_module.R:

library(shiny)

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

myModule <- function(input, output, session) {
  output$test <- renderUI({
    lapply(1:3, function(val) {
      fluidRow(column(12,renderText(paste("Line", val))))
    })
  })
}

ui <- fixedPage(
  h2("Module example"),
  myModuleUI("test_module")
)

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

shinyApp(ui, server)

All of the div elements are being created, but they just fail to contain the plots/text. How do I properly use the Shiny renderText() or renderPlot() functions within renderUI()/lapply() calls within a module?

like image 661
T. Thomas Avatar asked Apr 06 '17 15:04

T. Thomas


1 Answers

It appears that the approach I was taking using the renderText() and renderPlot() functions directly in a renderUI() works fine in the normal case, i.e. when not operating within a Shiny Module. Shiny automatically calls the necessary textOutput() or plotOutput() to generate the HTML. Some how this automatic link is broken when you are performing the same operations within a Shiny Module. I suspect this is due to the mismatch between assigning and referencing items in the output list due to the introduction of the ns() call when assigning outputIds as is done manually in a call to outputPlot() or outputText().

To successfully use renderUI within a Shiny Module, you need to separately call textOutput() and renderText(): textOutput in the lapply() in the renderUI(), and renderText() in an lapply() in an observe(). This allows us to introduce an ns() into the generation of the outputId for the textOutput() call.

Below I've included a refactoring of both app_normal.R and app_module.R that demonstrates the disentanglement of these two calls.

app_normal_observe.R:

library(shiny)

ui <- fixedPage(
  h2("Normal example"),
  uiOutput("test")
)

server <- function(input, output, session) {
  output$test <- renderUI({
    lapply(1:3, function(val) {
      fluidRow(column(12,textOutput(paste0("line_", val))))
    })
  })

  observe({
    lapply(1:3, function(val) {
      output[[paste0("line_", val)]] <- renderText(paste("Line", val))
    })
  })
}

shinyApp(ui, server)

app_module_observe.R:

library(shiny)

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

myModule <- function(input, output, session) {
  output$test <- renderUI({
    lapply(1:3, function(val) {
      fluidRow(column(12,textOutput(session$ns(paste0("line_", val)))))
    })
  })

  observe({
    lapply(1:3, function(val) {
      output[[paste0("line_", val)]] <- renderText(paste("Line", val))
    })
  })
}

ui <- fixedPage(
  h2("Module example"),
  myModuleUI("test_module")
)

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

shinyApp(ui, server)
like image 149
T. Thomas Avatar answered Nov 18 '22 23:11

T. Thomas