4

I'm trying to generate a UI element that is dynamic based on how the user wishes to provide their input. I'm using the Shiny Dashboard for simplicity but I have encountered an issue with the sidebarMenu. Previously when I was placing static UI elements directly into the sidebarMenu, I had no issue, however when trying to place dynamic UI elements into the sidebarMenu I have problems. I'm using R 3.3.2 and Shiny 1.0.0 and Dashboard 0.5.3.

The specific problem I have is that when the program first loads up, the dynamic UI elements do not load. There does not appear to be any holdup in the code, as all the features of the interface work fine even while the dynamic UI is unloaded. I am able to get the dynamic UI to load by selecting one of the tabs in the navbar, or by hovering over something that I have implemented a tooltip on.

I am unable to provide the exact code but I have recreated a much smaller reproducible example that has all the same problems that my larger version does.

library("shiny")
library("shinydashboard")

header = dashboardHeader(
    title = "Dynamic UI Example"
)
sidebar = dashboardSidebar(
    sidebarMenu(
        menuItemOutput("dynamic_sidebar")
    )
)
body = dashboardBody(
    tabBox(
        tabPanel(
            strong("One")
        ),
        tabPanel(
            strong("Two")
        )
    )
)
ui = dashboardPage(header, sidebar, body)

server = shinyServer(function(input,output,session){
output$dynamic_sidebar = renderMenu({
        sidebarMenu(
            menuItem(
                "Slider or numeric problem",
                radioButtons("slider_or_numeric",
                   label = "Slider or Numeric Input",
                   choices = c("Slider", "Numeric"),
                   selected = "Slider",
                   inline = TRUE
                ),
                uiOutput("input")
            )     
        )
    })
    output$input = renderUI({
        if (input$slider_or_numeric == "Slider"){
            sliderInput("slider", 
                label = "slider",
                min = 0, max = 1,
                value = 0
            )
        } else {
            numericInput("numeric", 
                label = "numeric",
                min = 0, max = 1,
                value = 0
            )
        }
    })
})

shinyApp(ui, server)

To verify the problem, after loading it up just open the menu item and you'll see the radio buttons but nothing else. Switch the tab on the navbar from one to two, and the input should appear in the menu (must be done while the menu is open).

I'm really just grasping at straws here, I have been troubleshooting this for hours and I think it's just an incompatibility with these features. I'm really hoping someone can prove me wrong and show me that I'm just doing it wrong. I've already found alternatives for my main program but they don't have the same aesthetic with what I'm trying to accomplish here.

Thanks for any and all help!

zach
  • 202
  • 1
  • 10

2 Answers2

2

I think I found the issue, it was tricky, and took me awhile to find it. Although it is a small change, I will post the whole thing.

I think this is what you want:

library("shiny")
library("shinydashboard")

header = dashboardHeader(
  title = "Dynamic UI Example"
)
sidebar = dashboardSidebar(
  sidebarMenu(
    menuItemOutput("dynamic_sidebar")
  )
)
body = dashboardBody(
  tabBox(
    tabPanel(
      strong("One")
    ),
    tabPanel(
      strong("Two")
    )
  )
)
ui = dashboardPage(header, sidebar, body)

server = shinyServer(function(input,output,session){
  output$dynamic_sidebar = renderMenu({
    sidebarMenu(
      menuItem(
        "Slider or numeric problem",
        radioButtons("slider_or_numeric",
                     label = "Slider or Numeric Input",
                     choices = c("Slider", "Numeric"),
                     selected = "Slider",
                     inline = TRUE
        )
      ),   
      uiOutput("input")  # no longer in menuItem call
    )
  })
  output$input = renderUI({
    if (input$slider_or_numeric == "Slider"){
      sliderInput("slider", 
                  label = "slider",
                  min = 0, max = 1,
                  value = 0
      )
    } else {
      numericInput("numeric", 
                   label = "numeric",
                   min = 0, max = 1,
                   value = 0
      )
    }
  })
})
shinyApp(ui, server)

I think the problem was that uiOutput("input") somehow slipped into the menuItem call. This is a hazard with Shiny, and Shiny editors obviously need more support for showing this kind of scope.

Frankly I am a bit puzzled as to why it ever showed up when you played around with it. But enough...

While I am not completely sure I understand how you want this to work (it is just a toy example obviously), this is how it looks now at startup:

enter image description here

And here after the menu expansion and changing the input type via the newly exposed radio button control.

enter image description here

Mike Wise
  • 22,131
  • 8
  • 81
  • 104
  • The inputs were inside the menuItem because that's where I want it to show up in the UI. I have several sidebarMenus and which one is displayed changes with a dropdown selection at the top of the sidebar. Within each sidebarMenu I have 4 to 12 input options and so I group them in menuItems 4 at a time. This way I can fit all 12 in the sidebar without displaying 12 things at once, the user can select what input category they want to alter. My solution so far has been to put the sliders and the numerics in different menuItems and let them coexist, but it's not the same visual I was hoping for. – zach Mar 29 '17 at 15:40
  • (New here, I hope it's not bad practice to double comment!) I've more or less given up on this as a solution, I've toyed around with many different things and I don't think it's possible to do what I want to do. I just came here as a last resort in case somebody had seen this specific problem before and found a solution. I appreciate your efforts! – zach Mar 29 '17 at 15:42
2

So I have found a solution from another SO post: r shiny - uiOutput not rendering inside menuItem and I'm slightly ashamed I did not manage to find this in my first set of search efforts. To sum up what the post says in more detail, the UI element I am rendering inside the menuItem is initialized as hidden, and hidden items are suspended by default. As the post details, you can change this option via the outputOptions function. Here is an updated version that works exactly as I wanted it to originally:

library("shiny")
library("shinydashboard")

header = dashboardHeader(
    title = "Dynamic UI Example"
)
sidebar = dashboardSidebar(
    sidebarMenu(
        menuItemOutput("dynamic_sidebar")
    )
)
body = dashboardBody(
    tabBox(
        tabPanel(
            strong("One")
        ),
        tabPanel(
            strong("Two")
        )
    )
)
ui = dashboardPage(header, sidebar, body)

server = shinyServer(function(input,output,session){

    output$input <- renderUI({})
    outputOptions(output, "input", suspendWhenHidden = FALSE)

output$dynamic_sidebar = renderMenu({
        sidebarMenu(
            menuItem(
                "Slider or numeric problem",
                radioButtons("slider_or_numeric",
                   label = "Slider or Numeric Input",
                   choices = c("Slider", "Numeric"),
                   selected = "Slider",
                   inline = TRUE
                ),
                uiOutput("input")
            )     
        )
    })
    output$input = renderUI({
        if (input$slider_or_numeric == "Slider"){
            sliderInput("slider", 
                label = "slider",
                min = 0, max = 1,
                value = 0
            )
        } else {
            numericInput("numeric", 
                label = "numeric",
                min = 0, max = 1,
                value = 0
            )
        }
    })
})

shinyApp(ui, server)

Note that it is necessary to initialize the output object BEFORE setting it's options or else it would be null and cause an error.

Thanks to Mike for attempting to answer the question, hopefully if someone else stumbles onto this problem they'll find this solution useful.

Community
  • 1
  • 1
zach
  • 202
  • 1
  • 10