is it possible to call modules in the ui conditionally?

703 views
Skip to first unread message

Stevan Earl

unread,
Aug 8, 2017, 1:43:02 PM8/8/17
to Shiny - Web Framework for R
Hello,

I am working on a Shiny application that allows a user to view and edit tables created from a database. I am using a module to generate a data table, and up to three data tables are generated depending on the number of records (1, 2, or 3) in the database. To streamline the user interface, I would like to call modules conditionally based on the number of database records that exist. So, for example, if there are only two records in the database, then only data tables for those first two records should be generated. I am having some difficulty accomplishing this in my ui. I can get something like the following to work where I can use conditions to call a single, independent record:
 
ui
column
(10,
       conditionalPanel
(
         condition
= "output.sampleList.length <= 1",
         dataTableUI
("one")
       
),
       conditionalPanel
(
         condition
= "output.sampleList.length <= 2",
         dataTableUI
("two")
       
),
       conditionalPanel
(
         condition
= "output.sampleList.length <= 3",
         dataTableUI
("three")
       
)
)


However, what I really need is something more like the following where the condition dictates the number of modules that are displayed. The approach below does not error but yields six empty modules. It does not seem that it is feasible to refer to modules more than once even if they are isolated in conditional panels. Is there another way to address this type of functionality? I know a loop would be another way to handle this but that has its own complications, especially for styling, and would prefer a conditional approach if it is possible.


column(10,
       conditionalPanel
(
         condition
= "output.sampleList.length <= 1",
         dataTableUI
("one")
       
),
       conditionalPanel
(
         condition
= "output.sampleList.length <= 2",
         dataTableUI
("one"),
         dataTableUI
("two")
       
),
       conditionalPanel
(
         condition
= "output.sampleList.length <= 3",
         dataTableUI
("one"),
         dataTableUI
("two"),
         dataTableUI
("three")
       
)
)


# truncated main app server code
server <- function(input, output) {
 
  samples
<- reactive({
   
    id
<- call to database....
   
return(id)
   
 
})
 
  callModule
(dataTableUI, "one", site, reactive({ samples()[1,'id'] }))
  callModule
(dataTableUI, "two", site, reactive({ samples()[2,'id'] }))
  callModule
(dataTableUI, "three", site, reactive({ samples()[3,'id'] }))
 
  output$sampleList
<- reactive({
    samples
()[,'id']
 
})
 
  outputOptions
(output, "sampleList", suspendWhenHidden = FALSE)
 
}




# truncated module code
dataTableUI <- function(id) {
  ns
<- NS(id)
 
...
}

dataTable
<- function(input, output, session, site, sampleId) {
 
...  
}



Thank you for any suggestions!

Bárbara Borges

unread,
Aug 8, 2017, 4:31:40 PM8/8/17
to Shiny - Web Framework for R
Why not this?

column(10,
  conditionalPanel
("output.sampleList.length >= 1",
    dataTableUI
("one")
 
),
  conditionalPanel
("output.sampleList.length >= 2",
    dataTableUI
("two")
 
),
  conditionalPanel
("output.sampleList.length >= 3",

    dataTableUI
("three")
 
)
)

Stevan Earl

unread,
Aug 8, 2017, 6:10:25 PM8/8/17
to Shiny - Web Framework for R

Hi Barbara,

Thanks so much for responding. Yes, yours seems like a perfectly logical solution. However, I get a similar result with that approach as I had when attempting to address this with a loop. Using either approach, what I wind up with in both cases is an "empty" module where there is not a record in the database. This is difficult to articulate, and I have attached a screen shot ('multipleModules.png') of the app that will hopefully help to clarify. In this particular example, there is a single record in the data base. We see those data in the top frame of the right, wide column where there is a data table populated with two rows of specimen data. However, the structure or frame work (not sure the appropriate term) of two other modules are also evident on the screen (each module is separated by two horizontal bars). So, even though there is only one record in the data base for this particular observation, which we see, we still see also, the 'common name', 'taxon', 'quantity' fields, and 'update' and 'add organism' action button outlines for two more modules. Ideally, we would not see any input or output components when there is not a corresponding record in the database.


Stevan
multipleModules.png

Bárbara Borges

unread,
Aug 8, 2017, 6:24:54 PM8/8/17
to Shiny - Web Framework for R
Can you please create a minimal repro (if you need to know what that means for Shiny, see this article)? Without it, it's hard to understand why my method wouldn't work...

Stevan Earl

unread,
Aug 9, 2017, 4:39:56 PM8/9/17
to Shiny - Web Framework for R
Hello Barbara,

Thanks again for your thoughtful assistance. I think, in fact, that your method does work - I think the problem is with my javascript. I am attempting to use *.length to determine the number of records in the database but the more I play with it the less sure that I am returning what I expect. This app is intertwined with a massive postgres database so producing a reproducible example would be very challenging. However, I have included a bit more of the relevant code below with comments and wonder if you might suggest how I should be determining the number of records that I might pass into my conditional panel to use with your earlier suggestion? In fact, is there a way to view the value of output.sampleList.length (I am new to javascript)?


Thank you,
Stevan

ui

# the conditional panel per your earlier suggestion

column
(10,
       conditionalPanel
("output.sampleList.length >= 1",
                        dataTableUI
("one")
       
),
       conditionalPanel
("output.sampleList.length >= 2",
                        dataTableUI
("two")
       
),
       conditionalPanel
("output.sampleList.length >= 3",
                        dataTableUI
("three")
       
)
)







server
<- function(input, output) {

# query sweepnet sample data for selected site

# safe to ignore all the sql, the important point is that it is returning a
# dataframe with one column (sweepnet_sample_id), and 0 to 3 rows depending on
# the number of records that are present in the database

sweepnetSamples
<- reactive({
 
 
# add listener for adding a sweepnet sample
  ssadd$sampleAdded  
 
  pg_local
<- databaseConn()
 
  baseSweepnetSamplesQuery
<- "
      SELECT
      --  se.survey_id,
        ss.sweepnet_sample_id
      FROM survey200.sweepnet_samples ss
      JOIN survey200.sampling_events se ON (ss.survey_id = se.survey_id)
      JOIN survey200.sites sites ON (sites.site_id = se.site_id)
      WHERE
        EXTRACT (YEAR FROM se.samp_date) = ?year AND
        sites.site_code LIKE ?selectedSite
      ORDER BY ss.sweepnet_sample_id ASC;"

 
  sweepnetSamplesQuery
<- sqlInterpolate(ANSI(),
                                         baseSweepnetSamplesQuery
,
                                         year
= surveyYear,
                                         selectedSite
= input$s200site)
  sweepnetSampleIds
<- dbGetQuery(pg_local, sweepnetSamplesQuery)
 
  dbDisconnect
(pg_local)
 
 
return(sweepnetSampleIds)
 
})




# create a vector (from 0 to 3) of records returned by the SQL query in
# sweepnetSamples() to pass as a list of samples for review by the user in the
# UI, and to use in conditional panels

output$sampleList
<- reactive({
 
  sweepnetSamples
()[,'sweepnet_sample_id']
 
})




# do not suspend hidden values

outputOptions
(output, "sampleList", suspendWhenHidden = FALSE)

Bárbara Borges

unread,
Aug 9, 2017, 5:01:02 PM8/9/17
to Shiny - Web Framework for R
Since I'm not familiar with the app or your database, it's hard for me to tell what would be the right query to produce what you need. However, I'd encourage you to use DBI + dplyr + pool if you are working with databases in Shiny. Here are some resources if you want:
However, looking at your code above, if all you want is to know how many rows are in the data frame that is returned (if it is a data frame), why not just do:

output$sampleList <- reactive({
 
  nrow
(sweepnetSamples())
 
})

(or create a different output just for the purpose of storing the relevant number...)?

Stevan Earl

unread,
Aug 9, 2017, 5:10:07 PM8/9/17
to Shiny - Web Framework for R
Yes, for sure, I was well into this app when I came across your pool package but will use that next time for sure!

Right, all I need is the number of records for use in the conditional panel. In fact I was trying to use something like you had suggested (e.g., nrow(sweepnetSamples())), but how would I reference that output for comparison in the conditional panel?

Bárbara Borges

unread,
Aug 9, 2017, 5:20:15 PM8/9/17
to Shiny - Web Framework for R
So if you have this in your server function (and assuming that sweepnetSamples is a data frame):

output$sampleList <- reactive({
  nrow
(sweepnetSamples())
})

You can have something like the following in the UI function:

column(10,
  conditionalPanel
("output.sampleList >= 1",
    dataTableUI
("one")
 
),
  conditionalPanel
("output.sampleList >= 2",
    dataTableUI
("two")
 
),
  conditionalPanel
("output.sampleList >= 3",


    dataTableUI
("three")
 
)
)

Stevan Earl

unread,
Aug 9, 2017, 5:30:08 PM8/9/17
to Shiny - Web Framework for R
Just awesome - nice that it works but frustrating to have been so close. Thank you sooooo much, Barbara!
Reply all
Reply to author
Forward
0 new messages