简体   繁体   中英

How to customize tabPanel added by clicking a button in Shiny

I have a sidebarLayout app in which I've set up buttons to add and remove tabPanel s in the sidebarPanel . However, I can't figure out how to customize those tabPanels. My code is below:

library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(width = 3, fixed=T,
      h3("L2 Machine"),
      actionButton('moreL2', tags$b('Add L2')),
      actionButton('lessL2', tags$b('Remove L2')),
      uiOutput('panelset'),
      tabPanel("L2panel", 
        numericInput(inputId='L2amount', 'Select L2 Amount', value=0),
        selectInput(inputId='L2type', 'Select L2 Type', c('Percent', 'Absolute')),
        uiOutput('L2daterange')
      )
    ),
    mainPanel(
      verbatimTextOutput('L2a'),
      verbatimTextOutput('L2t')
    )
  )
)

server <- function(input, output) {

  output$L2a <- renderPrint(input$L2amount)
  output$L2t <- renderPrint(input$L2type)

  output$panelset <- renderUI({
    n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))
    tabList <- lapply(paste("Pan", n), tabPanel)
    do.call(tabsetPanel, tabList)
  })

  output$L2daterange <- renderUI({
    dateRangeInput(inputId='L2daterange', 
      label='Select Adjustment Period', 
      start='01-01-2010', end='01-12-2015'
    )
  })
}

shinyApp(ui, server)

Currently, I have numericInput() , selectInput() , and uiOutput() inside tabPanel() . Instead I want each tabPanel created by clicking the button "Add L2" to have it's own set of numericInput , selectInput and uiOutput .

You create indeed different tabPanels but they are empty - both numericInput and selectInput are not inside of dynamic tabPanels. The solution is based on https://gist.github.com/wch/5436415/ and you can find there an extensive explanation why do you need a function local to render outputs with a for loop.


As said above, you created correctly dynamic tabPanels but they are empty. Within lapply you should specify unique widgets as arguments to tabPanel .

output$panelset <- renderUI({
    n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))
    tabList <- lapply(paste("Pan", n), tabPanel)
    do.call(tabsetPanel, tabList)
  })

Here I coded an example of how you can do it the correct way. Each time you create a unique tabPanel with a unique set of widgets.

tabList <- lapply(n, function(i) {
      tabPanel(
        title = paste0('Pan', i),

        numericInput(inputId = paste0('L2amount', i), 'Select L2 Amount', value = 0),

        selectInput(inputId = paste0('L2type', i), 'Select L2 Type', c('Percent', 'Absolute')),

        dateRangeInput(inputId = paste0('L2daterange',i), 
                     label = 'Select Adjustment Period', 
                     start = '01-01-2010', end = '01-12-2015'))

    })
    do.call(tabsetPanel, tabList)

  })

Then for each tabPanel with unique set of widgets you have to create unique set of outputs and then you can render values of your widgets.


Full solution:

library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(width = 3, fixed=T,
                 h3("L2 Machine"),
                 actionButton('moreL2', tags$b('Add L2')),
                 actionButton('lessL2', tags$b('Remove L2')),
                 uiOutput('panelset') 
    ),
    mainPanel(
      uiOutput("dynamic")
    )
  )
)


TMAX <- 10 # specify maximal number of dynamic panels
server <- function(input, output) {



  output$panelset <- renderUI({

    n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))

    # You have to create each time a new set of unique widgets
    tabList <- lapply(n, function(i) {
      tabPanel(
        title = paste0('Pan', i),

        numericInput(inputId = paste0('L2amount', i), 'Select L2 Amount', value = 0),

        selectInput(inputId = paste0('L2type', i), 'Select L2 Type', c('Percent', 'Absolute')),

        dateRangeInput(inputId = paste0('L2daterange',i), 
                     label = 'Select Adjustment Period', 
                     start = '01-01-2010', end = '01-12-2015'))

    })
    do.call(tabsetPanel, tabList)

  })


    output$dynamic <- renderUI({
      n <- seq(max(input$moreL2 - input$lessL2 + 1, 1))

      # You want to render n-times different outputs and each time you have 
      # k different outputs -- > need a list within a list. 
      lapply(n, function(i) {

        list(
          h5(paste0("Pan", i, " widgets")),

          verbatimTextOutput(paste0('L2a', i)),
          verbatimTextOutput(paste0('L2t', i)),
          verbatimTextOutput(paste0('L2dat', i)),

          br()
        )
      })
    })

    for (i in 1:TMAX) {

      local({
        my_i <- i

        # Outputs
        L2a <- paste0('L2a', my_i)
        L2t <- paste0('L2t', my_i)
        L2dat <- paste0('L2dat', my_i)

        list( 
          output[[L2a]] <- renderPrint({ input[[paste0('L2amount', my_i)]] }),

          output[[L2t]] <- renderPrint({ input[[paste0('L2type', my_i)]] }),

          output[[L2dat]] <- renderPrint({ input[[paste0('L2daterange', my_i)]] })
        )
      })
    }

}

shinyApp(ui, server)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM