Larsq
Larsq

Reputation: 365

How to InsertUI in a modularized shiny application?

I'm trying to build an application, where the user can add a piece of UI (in this case an input text field) by clicking a button. As this is only part of a bigger application, I wish to use modules to keep structure in my project.

However, my text field won't show after clicking the action button. I'm using the new function moduleServer() that was recently introduced by shiny.

Here is a reprex

library(shiny)

ModularizedUI <- function(id) {
  ns <- NS(id)
  
  fluidPage(
    # Input: Action button to add text field
    actionButton(inputId = ns("add_text"),
                 label = "Add text field"),
  )
}

ModularizedServer <- function(id) {
  moduleServer(
    id,
    function(input, output, session) {
      
      ns <- session$ns
      
      # Track the number of input boxes to render for test training
      counter_text <- reactiveVal(0)
      
      # Input/Output fields for start dates test training
      observeEvent(input$add_text, {
        
        counter_text(counter_text() + 1)
        
        # Add UI if this button is clicked
        insertUI(
          selector = "#add",
          where = "afterEnd",
          ui = textInput(inputId = ns(paste0("textfield_", counter_text())), 
                         label = "New text")
        )
      })
    }
  )
}

ui <- navbarPage("Dashboard",
                 tabPanel("Text fields",
                          ModularizedUI(id = "Text_Fields")
                 )
                 
)

server <- function(input, output, session) {
  ModularizedServer("Text_Fields")
}

shinyApp(ui = ui, server = server)

Any help is appreciated!

Upvotes: 1

Views: 291

Answers (1)

Martin Schmelzer
Martin Schmelzer

Reputation: 23919

You gave insertUI the selector #add, which defines where the new elements have to be inserted. But there is no element with id #add in your UI.

Add div(id = '#add') after your actionButton and it should work.

(I would then use where = beforeEnd, so that all Inputs are inside that new div and can be easily targeted using CSS or JS, or....)

Complete example:

library(shiny)

ModularizedUI <- function(id) {
  ns <- NS(id)
  
  fluidPage(
    # Input: Action button to add text field
    actionButton(inputId = ns("add_text"),
                 label = "Add text field"),
    div(id = "add")
  )
}

ModularizedServer <- function(id) {
  moduleServer(id, function(input, output, session) {
      ns <- session$ns
      
      # Track the number of input boxes to render for test training
      counter_text <- reactiveVal(0)
      
      # Input/Output fields for start dates test training
      observeEvent(input$add_text, {
        counter_text(counter_text() + 1)
        
        # Add UI if this button is clicked
        insertUI(
          selector = "#add",
          where = "beforeEnd",
          ui = textInput(inputId = ns(paste0("textfield_", counter_text())), 
                         label = "New text")
        )
      })
    }
  )
}

ui <- navbarPage("Dashboard",
                 tabPanel("Text fields",
                          ModularizedUI(id = "Text_Fields")))

server <- function(input, output, session) {
  ModularizedServer("Text_Fields")
}

shinyApp(ui = ui, server = server)

Upvotes: 2

Related Questions