Skip to content

Instantly share code, notes, and snippets.

@daattali
Forked from geebioso/accordion_reprex
Last active June 20, 2019 21:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daattali/75dc075f173ba4b11305ed53e78a83a2 to your computer and use it in GitHub Desktop.
Save daattali/75dc075f173ba4b11305ed53e78a83a2 to your computer and use it in GitHub Desktop.
This code creates deletable dynamic accordions that load a modal when they are created. The modal is triggering too many times after an accordion is deleted and then reloaded.
makeReactiveTrigger <- function() {
rv <- shiny::reactiveValues(a = 0)
list(
depend = function() {
rv$a
},
trigger = function() {
rv$a <- shiny::isolate(rv$a + 1)
}
)
}
library(shiny)
library(glue)
ui <- fluidPage(
tags$div(id = "RowPlaceholder"),
actionButton('add', label = "Add 2 accordions"),
actionButton('delete', label = "Delete accordions")
)
server <- function(input, output, session) {
#!!! you can change the number of batteries if it helps debug
rows_to_create <- c("name1", "name2")
current_accordions <- reactiveVal()
auto_pop_modal <- modalDialog(h2("Auto Populating"), footer = NULL)
delete_trigger <- makeReactiveTrigger()
delete_all <- reactive({
input$delete
delete_trigger$depend() # trigger off the auto_populate_btn actionButton
})
num <- reactiveVal(0)
insert_accordion <- function(
row_name,
delete = delete_all,
auto_pop_modal) {
if (row_name %in% current_accordions()) return()
current_accordions(c(current_accordions(), row_name))
accordion_item_div_id <- glue::glue("accordion_item_div_{row_name}")
accordion_item_id <- glue::glue("accordion_item_{row_name}")
delete_btn_id <- glue::glue("delete_{row_name}")
accordion_item <- div(
id = accordion_item_div_id,
accordion_item_div_id,
actionButton(delete_btn_id, NULL, icon = icon("trash"))
)
insertUI(
selector = glue::glue("#{'RowPlaceholder'}"),
where = "beforeEnd",
ui = accordion_item,
immediate = TRUE
)
# !!! Dean - this observeEvent will eventually be inside a sub-module that is called
# for each accordion. The idea is to pass the modal through to a sub-module
# and have it display when we are peforming some computations there. I pulled
# it out of the sub-module because we still see the bug here
observeEvent(input$add, {
showModal(auto_pop_modal)
Sys.sleep(0.3)
removeModal()
})
delete_accordion <- function(delete_btn_id){
delete_accordion_num <- strsplit(delete_btn_id, "delete_")[[1]][2]
accordion_item_div_id <- glue::glue("accordion_item_div_{delete_accordion_num}")
delete_btn_id <- glue::glue("delete_{delete_accordion_num}")
removeUI(selector = glue::glue("#{accordion_item_div_id}"), immediate = TRUE)
shinyjs::runjs(glue::glue("Shiny.onInputChange(‘{accordion_item_div_id}’, null)"))
shinyjs::runjs(glue::glue("Shiny.onInputChange(‘{delete_btn_id}’, null)"))
current_accordions(
current_accordions()[-match(delete_accordion_num, current_accordions())]
)
}
observeEvent(input$add, {
delete_trigger$trigger()
}, ignoreInit = TRUE, priority = 2)
observeEvent(input[[delete_btn_id]],{
delete_accordion(delete_btn_id)
}, once = TRUE, ignoreInit = TRUE)
observeEvent(delete(),{
delete_accordion(delete_btn_id)
}, once = TRUE, ignoreInit = TRUE, priority = 2)
}
observeEvent(input$add, {
lapply(rows_to_create, function (name) {
insert_accordion(
row_name = name,
delete = delete_all,
auto_pop_modal
)
})
}, priority = 1)
}
shinyApp(ui, server)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment