R Shiny:在 navbarPage() 中动态创建带有输出的选项卡

R Shiny: Dynamically creating tabs with output within navbarPage()

我正在尝试使用 navbarPage()(或类似的东西)创建一个应用程序,您可以在边栏中选择某些输入,当您点击一个按钮时,它会在单独的选项卡中显示结果。我使用下面的 script of K.Rohde 创建了一个示例(请注意,我在脚本中留下了他的原始评论)。

在此示例中,您在边栏中选择了 4 个字母,如果您单击该按钮,它会动态创建一个带有文本输出的单独选项卡。当我使用 fluidPage() 时效果很好,但我想使用 navbarPage() 或类似的东西,因为我的最终脚本包含更多页面。

当我使用 navbarPage() 时,脚本不再工作:

我尝试通过在 ui 和服务器中使用 tabsetPanel()tabPanel 来修复它,但这没有用。 SBista 认为 navbarPage() 似乎搞乱了 Javascript 功能,正如我在 .

中提到的

如有任何帮助,我将不胜感激!

ui:

ui <- navbarPage("Shiny",

  # Important! : JavaScript functionality to add the Tabs
  tags$head(tags$script(HTML("
                             /* In coherence with the original Shiny way, tab names are created with random numbers. 
                             To avoid duplicate IDs, we collect all generated IDs.  */
                             var hrefCollection = [];

                             Shiny.addCustomMessageHandler('addTabToTabset', function(message){
                             var hrefCodes = [];
                             /* Getting the right tabsetPanel */
                             var tabsetTarget = document.getElementById(message.tabsetName);

                             /* Iterating through all Panel elements */
                             for(var i = 0; i < message.titles.length; i++){
                             /* Creating 6-digit tab ID and check, whether it was already assigned. */
                             do {
                             hrefCodes[i] = Math.floor(Math.random()*100000);
                             } 
                             while(hrefCollection.indexOf(hrefCodes[i]) != -1);
                             hrefCollection = hrefCollection.concat(hrefCodes[i]);

                             /* Creating node in the navigation bar */
                             var navNode = document.createElement('li');
                             var linkNode = document.createElement('a');

                             linkNode.appendChild(document.createTextNode(message.titles[i]));
                             linkNode.setAttribute('data-toggle', 'tab');
                             linkNode.setAttribute('data-value', message.titles[i]);
                             linkNode.setAttribute('href', '#tab-' + hrefCodes[i]);

                             navNode.appendChild(linkNode);
                             tabsetTarget.appendChild(navNode);
                             };

                             /* Move the tabs content to where they are normally stored. Using timeout, because
                             it can take some 20-50 millis until the elements are created. */ 
                             setTimeout(function(){
                             var creationPool = document.getElementById('creationPool').childNodes;
                             var tabContainerTarget = document.getElementsByClassName('tab-content')[0];

                             /* Again iterate through all Panels. */
                             for(var i = 0; i < creationPool.length; i++){
                             var tabContent = creationPool[i];
                             tabContent.setAttribute('id', 'tab-' + hrefCodes[i]);

                             tabContainerTarget.appendChild(tabContent);
                             };
                             }, 100);
                             });
                             "))),
  # End Important

  tabPanel("Statistics"),

  tabPanel("Summary",
    sidebarLayout(
      sidebarPanel(width = 4,
                 selectInput(inputId = "choice_1", label = "First choice:",
                             choices = LETTERS, selected = "H", multiple = FALSE),
                 selectInput(inputId = "choice_2", label = "Second choice:",
                             choices = LETTERS, selected = "E", multiple = FALSE),
                 selectInput(inputId = "choice_3", label = "Third choice:",
                             choices = LETTERS, selected = "L", multiple = FALSE),
                 selectInput(inputId = "choice_4", label = "Fourth choice:",
                             choices = LETTERS, selected = "P", multiple = FALSE),
                 actionButton("goCreate", "Go create a new Tab!")
    ), 
    mainPanel(
      tabsetPanel(id = "mainTabset",
                  tabPanel("InitialPanel1", "Some text here to show this is InitialPanel1",
                           textOutput("creationInfo"),
                           # Important! : 'Freshly baked' tabs first enter here.
                           uiOutput("creationPool", style = "display: none;")
                           # End Important
                  )
      )
    )
    )
  )
)

服务器:

server <- function(input, output, session){

  # Important! : creationPool should be hidden to avoid elements flashing before they are moved.
  #              But hidden elements are ignored by shiny, unless this option below is set.
  output$creationPool <- renderUI({})
  outputOptions(output, "creationPool", suspendWhenHidden = FALSE)
  # End Important

  # Important! : This is the make-easy wrapper for adding new tabPanels.
  addTabToTabset <- function(Panels, tabsetName){
    titles <- lapply(Panels, function(Panel){return(Panel$attribs$title)})
    Panels <- lapply(Panels, function(Panel){Panel$attribs$title <- NULL; return(Panel)})

    output$creationPool <- renderUI({Panels})
    session$sendCustomMessage(type = "addTabToTabset", message = list(titles = titles, tabsetName = tabsetName))
  }
  # End Important 

  output$creationInfo <- renderText({
    paste0("The next tab will be named: Results ", input$goCreate + 1)
  })

  observeEvent(input$goCreate, {
    nr <- input$goCreate

    newTabPanels <- list(
      tabPanel(paste0("NewTab ", nr),

               htmlOutput(paste0("Html_text", nr)),
               actionButton(paste0("Button", nr), "Some new button!"), 
               textOutput(paste0("Text", nr))
      )
    )

    output[[paste0("Html_text", nr)]] <- renderText({
        paste("<strong>", "Summary:", "</strong>", "<br>",
              "You chose the following letters:", isolate(input$choice_1), isolate(input$choice_2), isolate(input$choice_3), isolate(input$choice_4), "." ,"<br>",
              "Thank you for helping me!")
    })

    addTabToTabset(newTabPanels, "mainTabset")
  })
}

正如我在 的评论中提到的 javascript 功能似乎存在一些问题,在进一步参考 HTML 结构后我发现 navbarPage有两个tab-contents。由于这个原因,javascript 失败了,所以稍微改变 javascript 功能似乎确实有效。

你只需要改变
var tabContainerTarget = document.getElementsByClassName('tab-content')[0];

var tabContainerTarget = document.getElementsByClassName('tab-content')[1];

因此,如果您的 ui 更改为新的 javascript,您的代码应该可以正常工作,如下所示:

ui <- navbarPage("Shiny",

                 # Important! : JavaScript functionality to add the Tabs
                 tags$head(tags$script(HTML("
                                            /* In coherence with the original Shiny way, tab names are created with random numbers. 
                                            To avoid duplicate IDs, we collect all generated IDs.  */
                                            var hrefCollection = [];

                                            Shiny.addCustomMessageHandler('addTabToTabset', function(message){
                                            var hrefCodes = [];
                                            /* Getting the right tabsetPanel */
                                            var tabsetTarget = document.getElementById(message.tabsetName);

                                            /* Iterating through all Panel elements */
                                            for(var i = 0; i < message.titles.length; i++){
                                            /* Creating 6-digit tab ID and check, whether it was already assigned. */
                                            do {
                                            hrefCodes[i] = Math.floor(Math.random()*100000);
                                            } 
                                            while(hrefCollection.indexOf(hrefCodes[i]) != -1);
                                            hrefCollection = hrefCollection.concat(hrefCodes[i]);

                                            /* Creating node in the navigation bar */
                                            var navNode = document.createElement('li');
                                            var linkNode = document.createElement('a');

                                            linkNode.appendChild(document.createTextNode(message.titles[i]));
                                            linkNode.setAttribute('data-toggle', 'tab');
                                            linkNode.setAttribute('data-value', message.titles[i]);
                                            linkNode.setAttribute('href', '#tab-' + hrefCodes[i]);

                                            navNode.appendChild(linkNode);
                                            tabsetTarget.appendChild(navNode);
                                            };

                                            /* Move the tabs content to where they are normally stored. Using timeout, because
                                            it can take some 20-50 millis until the elements are created. */ 
                                            setTimeout(function(){
                                            var creationPool = document.getElementById('creationPool').childNodes;
                                            var tabContainerTarget = document.getElementsByClassName('tab-content')[1];

                                            /* Again iterate through all Panels. */
                                            for(var i = 0; i < creationPool.length; i++){
                                            var tabContent = creationPool[i];
                                            tabContent.setAttribute('id', 'tab-' + hrefCodes[i]);

                                            tabContainerTarget.appendChild(tabContent);
                                            };
                                            }, 100);
                                            });
                                            "))),
                 # End Important

                 tabPanel("Statistics"),

                 tabPanel("Summary",
                          sidebarLayout(
                            sidebarPanel(width = 4,
                                         selectInput(inputId = "choice_1", label = "First choice:",
                                                     choices = LETTERS, selected = "H", multiple = FALSE),
                                         selectInput(inputId = "choice_2", label = "Second choice:",
                                                     choices = LETTERS, selected = "E", multiple = FALSE),
                                         selectInput(inputId = "choice_3", label = "Third choice:",
                                                     choices = LETTERS, selected = "L", multiple = FALSE),
                                         selectInput(inputId = "choice_4", label = "Fourth choice:",
                                                     choices = LETTERS, selected = "P", multiple = FALSE),
                                         actionButton("goCreate", "Go create a new Tab!")
                            ), 
                            mainPanel(
                              tabsetPanel(id = "mainTabset",
                                          tabPanel("InitialPanel1", "Some text here to show this is InitialPanel1",
                                                   textOutput("creationInfo"),
                                                   # Important! : 'Freshly baked' tabs first enter here.
                                                   uiOutput("creationPool", style = "display: none;")
                                                   # End Important
                                          )
                              )
                            )
                          )
                 )
                 )