5

How to hide/show several elements at once with shinyjs? In the following example my goal is to hide/show both tables with just two lines of code instead of four. Why do I want to do this? In reality, I am dealing with several tables and several events such that showing/hiding them all at once would keep the code a lot cleaner.

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  actionButton("hide","Hide!"),
  actionButton("show","Show!"),
  tableOutput("table1"),
  tableOutput("table2"))

server <- function(input, output, session) {
  output$table1 <- renderTable({head(iris)})
  output$table2 <- renderTable({head(iris)})
  observeEvent(input$hide, {hide("table1")})
  observeEvent(input$show, {show("table1")})
  observeEvent(input$hide, {hide("table2")})
  observeEvent(input$show, {show("table2")})}

shinyApp(ui, server)
Joe
  • 1,628
  • 3
  • 25
  • 39

2 Answers2

4

You can write a function that performs both operations and call them once per ui element that you want to hide/show.

library(shiny)
library(shinyjs)

toggleView <- function(input, output_name){
  observeEvent(input$show, {show(output_name)})
  observeEvent(input$hide, {hide(output_name)})
}

ui <- fluidPage(
  useShinyjs(),
  actionButton("hide","Hide!"),
  actionButton("show","Show!"),
  tableOutput("table1"),
  tableOutput("table2"))

server <- function(input, output, session) {
  output$table1 <- renderTable({head(iris)})
  output$table2 <- renderTable({head(iris)})
  toggleView(input, "table1")
  toggleView(input, "table2")
}

shinyApp(ui, server)

You can also go one step further and vercorize this function with respect to output_name. Make sure to use lapply rather than for however, since the latter can run into problems with lazy evaluation.

toggleViews <- function(input, output_names){
  lapply(output_names, function(output_name){
    toggleView(input, output_name)
  })
}

...

toggleViews(input, c("table1", "table2"))
Gregor de Cillia
  • 7,397
  • 1
  • 26
  • 43
  • Great! Why do we need `input` as an argument for `toggleView`? I tried it without, but it did not work. – Joe Oct 13 '17 at 16:25
  • 2
    If you define the `toggleView` function inside the server, the `input` argument should not be necessary. Alternatively, you can also do some environment hacking with `input = getDefaultReactiveDomain()$input` – Gregor de Cillia Oct 13 '17 at 16:25
  • Is there anything wrong with defining `toggleView` inside of `server`? – Joe Oct 13 '17 at 16:28
  • 1
    Putting function/object definitions outside the server (in `global.R`) makes sense if they (1) need a lot of time to run or (2) are used in `ui.R` and `server.R`. In this case, none of those two criteria are met. – Gregor de Cillia Oct 13 '17 at 16:31
  • 1
    I just edited the answer to allow vector inputs in `output_name`. Maybe you can save some additional lines of code this way. – Gregor de Cillia Oct 13 '17 at 17:05
2

Since all your tables' ids start with "table" you can use this pattern to tell shinyjs to hide/show all of them at once by using jQuery selectors. With the argument selector = "[id^='table']" you show/hide all instances of a div with an id that starts with "table". I show this example below.

jQuery selectors are flexible and there may be other ways to achieve this. Check out the documentation.

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  actionButton("hide","Hide!"),
  actionButton("show","Show!"),
  tableOutput("table1"),
  tableOutput("table2"))

server <- function(input, output, session) {
  output$table1 <- renderTable({head(iris)})
  output$table2 <- renderTable({head(iris)})
  
  observeEvent(input$hide, {hide(selector = "[id^='table']")})
  observeEvent(input$show, {show(selector = "[id^='table']")})
}

shinyApp(ui, server)
Jan
  • 4,974
  • 3
  • 26
  • 43