Using dynamic UI components when using Shiny modules

1,854 views
Skip to first unread message

Niels Schenk

unread,
Feb 12, 2016, 9:37:32 AM2/12/16
to Shiny - Web Framework for R
Hi,

We're using Shiny modules quite extensively and love it. There's one specific issue that we're running into. 

We have a Shiny app with several modules. Most of them are independent of each other but one is a special case of a 'nested' model (which isn't really nested). The situation is as follows: we have a tab with specific set of inputs and outputs. Given the structure of our layout, we are not able to render the inputs of the tab in the UI function of the tab itself. All of the inputs are in a separate filter module that renders the filters for a given tab depending on which tab is selected. This works perfectly, and allows us in the server part of a tab component to take dependencies on inputs as if they were shown by the UI function of that tab.

The issue is that we need dynamic UI components where it's impossible or very inefficient to use for example updateSelectInput. This means that certain inputs in the exported UI have to be dependent on inputs from the same exported UI. This does not work. It only works if we split up the exported UI into several renderUI parts. The issue with this is that our filters are different across tabs so that rendering the UI in the filter module become really tedious if we have to handle a dynamic number of renderUI components. 

I'm sure that this is all very confusing so I've put together three examples. Please see https://gist.github.com/Vestaxis/8a785b63c7b4566a2954

Any input would be greatly appeciated. 

Joe Cheng

unread,
Feb 12, 2016, 1:19:14 PM2/12/16
to Niels Schenk, Shiny - Web Framework for R
I don't think I'm going to articulate this well, but I'll try:

In your examples, you're splitting the uiOutput and the renderUI calls across distinct modules. That's something you don't want to do. Instead, endeavor to keep the uiOutput and renderUI defined in the same module.

The way you should inject one module A's UI into another module B's UI, is by actually passing tag objects (created by module A's UI function) into the B UI function. In other words, the actual uiOutput should belong to the "tab" module.

But there's already a tabUI function that has textOutput in it, and that needs to render somewhere different than inside the tab. How can you let a module put UI in two places on the page (one wherever the textOutput needs to go, and one inside the filter UI)? You can either split the module into two modules (and have them communicate through reactive expressions), or... you can have multiple UI functions for a single module! I've taken this latter approach in the gist below, where I updated all three of your examples:


It's OK to have an n-to-1 relationship between UI functions and module server functions. Just make sure that all the UI functions for a given module instance are invoked with the same id, as I do in the gist.

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/594624c2-a2d8-4593-8ade-81b9967c8025%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Niels Schenk

unread,
Feb 12, 2016, 2:00:32 PM2/12/16
to Shiny - Web Framework for R, ni...@increaseonline.com
Many thanks for your reply and the examples. I actually first split the 'tab modules' into two parts but this made it somewhat cumbersome to use the inputs (I simply want the filter inputs to be available in the module that uses it, rather than first importing them from another module). 

Your proposed solutions works perfectly, thanks for that. There is one twist that I left out of my examples (but what I tried to describe in my text), and that is that I want to adjust what filterUI is showing depending on the tab that is selected. I don't see how I would do the switching through the ui part of a module or my app (i.e. an change in filtersUI("filters", tabUI2("tab")) in your edit of app3.R that makes this dynamic based on an input).

I've added an app4.R to my gist (https://gist.github.com/Vestaxis/8a785b63c7b4566a2954#file-app4-r) showing how I would do that based on your feedback. I'd love to hear if you think that this for some reason is not necessary or not the best solution. Note that in this example I don't actually switch the UI part based on any change but in the actual app I take a dependency on an input indicating tab switches. 

Thanks again!


Niels Schenk

unread,
Feb 12, 2016, 2:51:34 PM2/12/16
to Shiny - Web Framework for R, ni...@increaseonline.com
Sorry, when putting together app4.R I didn't clean my environment so I didn't notice it had an error. I've now updated the example to a working version.

Joe Cheng

unread,
Feb 12, 2016, 3:28:42 PM2/12/16
to Niels Schenk, Shiny - Web Framework for R
OK. If I'm understanding you correctly, you're saying that plugging UI elements directly into filterUI won't work, because the filter module wants to dynamically choose between different modules, depending on which tab is active. For clarity, I'm going to call the set of different modules "subfilters" rather than "tab".

First off, I think we're getting very close to the point where the subfilter modules should be nested into the filter module. That is to say,

subfilter1UI <- function(id) {...}
subfilter1 <- function(input, output, session) { ... }
subfilter2UI <- function(id) {...}
subfilter2 <- function(input, output, session) { ... }

filterUI <- function(id) {
  ns <- NS(id)
  uiOutput("subfilter")
}
filter <- function(input, output, session, selectedTab) {
  callModule(subfilter1, "one")
  callModule(subfilter2, "two")
  output$subfilter <- renderUI({
    req(selectedTab())
    switch(selectedTab(),
      "One" = subfilter1UI(session$ns("one")),
      "Two" = subfilter2UI(session$ns("two"))
    }
  })
}

If you don't want to nest them like that, but want to still keep them separate for other reasons, then:

subfilter1UI <- function(id) {...}
subfilter1 <- function(input, output, session) { ... }
subfilter2UI <- function(id) {...}
subfilter2 <- function(input, output, session) { ... }

filterUI <- function(id) {
  ns <- NS(id)
  uiOutput("subfilter")
}
filter <- function(input, output, session, selectedTab, subfilters) {
  callModule(subfilter1, "one")
  callModule(subfilter2, "two")
  output$subfilter <- renderUI({
    req(selectedTab())
    subfilters[[selectedTab()]]
  })
}

# server function
function(input, output, session) {
  callModule(subfilter1, "one")
  callModule(subfilter2, "two")
  callModule(filter, "filter", reactive(input$tabset),
    list(
      "One" = subfilter1UI("one"),
      "Two" = subfilter2UI("two")
    )
  )
}

I'm really sorry that I can't articulate these ideas more clearly--I'd love to be able to boil this down to a simple set of rules or principles. I feel like a year from now these rules will have surfaced into my conscious mind, but right now, I'm fairly confident I can come up with the "right" solutions but have a really hard time saying what makes them right.

On Fri, Feb 12, 2016 at 11:51 AM, Niels Schenk <ni...@increaseonline.com> wrote:
Sorry, when putting together app4.R I didn't clean my environment so I didn't notice it had an error. I've now updated the example to a working version.

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.

Niels Schenk

unread,
Feb 12, 2016, 3:56:41 PM2/12/16
to Shiny - Web Framework for R, ni...@increaseonline.com
Again, many thanks for your input, I really appreciate it. 

That's exactly what I was trying to say, and your second suggestion is similar to how I've implemented the subfilters now. The main difference is that based on your suggestions I would inject a subfilter_x_UI2 into the filter module. 

There is one thing I don't completely understand. In your second suggestion you're importing the submodule function in both the filter function, and the server function by calling:

callModule(subfilter1, "one")
callModule(subfilter2, "two")

Can you please explain why it is necessary to do this in both functions? I would think that in this setup this is only necessary in the server function. 

Thanks

Niels Schenk

unread,
Feb 13, 2016, 4:32:40 AM2/13/16
to Shiny - Web Framework for R, ni...@increaseonline.com
Just to finish this off I've put together an example which does exactly what I want based on your input. It doesn't seem to be necessary to do the callModule() calls in the filter functions. See https://gist.github.com/Vestaxis/79abf2df98c9b543e729

MySchizo Buddy

unread,
Feb 20, 2016, 4:04:53 PM2/20/16
to Shiny - Web Framework for R, ni...@increaseonline.com
Please add these gist examples to the shiny modules documentation
Reply all
Reply to author
Forward
Message has been deleted
0 new messages