2

I'm working on a Shiny app where users can start from a table of data, and create new tables based on selected filters/summaries of the previous tables - something like this:

Table1 ---- (filter) > Table2
       ---- (filter) > Table3  ---- (filter) > Table4
                               ---- (filter) > Table5
       ---- (filter) > Table6

Using lists of lists in R, I made a tree data structure (called sliceBox.tree in my code example) to keep track of each table/node. Each table will come with a set of inputs as well: some filter settings and an actionButton, which can be used to create a new child table under that parent table.

I'm using the observeEvent() function to react to each button press, but when I try to append a node to the tree inside the observeEvent handler, the changes are only made to the copy tree inside the function, and the original tree is unaffected. Is there a way to edit the original tree from inside the observeEvent handler or maybe a different approach to do this?

Here is a trimmed-down version of my code now to illustrate the problem - I want the current code to create child actionButtons on the page by clicking on any existing buttons.

server <- function(input, output) {

  newNode <- function(id, parentId) {
    node <- list(
      parent = parentId, 
      children = list(),
      uiObject = createButton(id, parentId) 
    )
    return(node)
  }


  createButton <- function(id, parentId) {
    print(paste0("Creating button ", id))
    buttonID <- paste0("sliceBoxButton", id)
    return(actionButton(buttonID, paste0("I am ", buttonID, ", child of ", parentId), icon("plus-circle fa-2x")))
  }


  rootNode <- newNode(1, 0)
  sliceBox.tree <- list(rootNode) # We'll store our nodes as a 1D list, so parent and child ID's are recorded as their indices in the list
  output$debug <- renderPrint({
    print(paste0("Button presses: ", v$counter))
    print("FULL TREE")
    print(sliceBox.tree)
  })


  # Keep a total count of all the button presses (also used loosely as the number of tables created)
  v <- reactiveValues(counter = 1L) 

  # Event handlers: specific function for particular button will run when that button is pressed
  observeEvent(v$counter, {
    for (id in 1:v$counter) {
      buttonID <- paste0("sliceBoxButton", id)
      observeEvent(input[[buttonID]], {
        v$counter <- v$counter + 1L
        print(paste0("Pressed ", buttonID))


        # Append new child to list of children
        numChildren <- length(sliceBox.tree[[id]]$children)
        sliceBox.tree[[id]]$children[numChildren + 1] <- v$counter 


        sliceBox.tree[[v$counter]] <- newNode(v$counter, id)
        print(paste0("Appending node to tree at index ", v$counter))
        print(sliceBox.tree)
      })
    }
  })


  # renderUI needs a list of uiObjects, so we just extract the uiObjects from every node of the tree
  output$allSliceBoxes <- renderUI({
    table_output_list <- lapply(1:v$counter, function(i) { 
      return(getElement(sliceBox.tree[[i]], 'uiObject'))
    })
    do.call(tagList, table_output_list) # Convert the list to a tagList 
  })


}


ui <- fluidPage(
  uiOutput("allSliceBoxes"),
  verbatimTextOutput('debug')
)


shinyApp(ui = ui, server = server)

Really hope someone can help, I've been googling extensively but haven't found any examples that do something similar - been stuck on this for days!

1 Answer 1

1

One way to solve this would be to store your tree in a reactiveValues structure.

# sliceBox.tree <- list(rootNode)
# Use this instead:
sliceBox.tree <- reactiveValues(tree=list(rootNode))

From that point on, change every sliceBox.tree to sliceBox.tree$tree. It works for me.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.