Home > Net >  How to modify names of reactiveValues list elements from an observer?
How to modify names of reactiveValues list elements from an observer?

Time:12-27

This is my first time working with lists in R (only worked with dataframes so far) and I'm trying to understand how to access and change the different elements/levels/names in a list from a reactive framework. The MWE code posted at the bottom provides an action button that generates additional list elements and in a for-loop sequentially numbers one level of list element names, and in the R studio console while running this in Shiny you can see the evolution of the list.

My question, as illustrated below, is how do I access the names of the list elements for reactiveValues object uiTbl from the for-loop in the code, to for example, change the "div" in each name to "hello"? (Ultimately in the full code I'm going to resequence the numbering for each "div..." tag but I can do that later, for now I'm just trying to understand how to manipulate reactive lists). I'm trying to change the names of list elements from within the observe(). The code does this for one level of names ("Col..."), but I haven't been able to figure out how to go the higher list level names of "div...". In the illustration below, the black "explains" (haha) the current code output in R Studio console and the red shows what I'm trying to do.

My understanding of lists is so limited that my description above may be bungled, I am certainly eagerly receptive to corrections where I erred.

enter image description here

Code:

library(shiny)

data1 <- data.frame(row.names = c("A","B"),"Col 1"=c(1,2),check.names=FALSE)

ui <- fluidPage(hr(),actionButton("add","Add element"))

server <- function(input,output,session) {
  uiTbl <- reactiveValues(div_1_tbl = data1)
  
  observeEvent(input$add, {
    divID <- paste0("div_", input$add 1)
    uiTbl[[paste0(divID,"_tbl")]] <- data1 
  })
  
  observe({
    tables_list <- reactiveValuesToList(uiTbl)
    table_lengths <- lengths(tables_list)
    cumsum_table_lengths <- cumsum(table_lengths)[table_lengths != 0L]
    
    # below adds sequential numbering to the "Col" name for each list element
    for(i in seq_along(cumsum_table_lengths)){
      names(uiTbl[[names(cumsum_table_lengths[i])]]) <- paste("Col", cumsum_table_lengths[i])
      print(uiTbl[[names(cumsum_table_lengths[i])]]) # shows results of above in console
    }
    print(names(uiTbl)) # shows name tags of list elements (did I say this right?) in console
  })

}

shinyApp(ui, server)

CodePudding user response:

It seems that the Shiny framework explicitly wants to exclude this use case. A lot of observations point to that assumption. The implementation does a lot to make it impossible to change the names of reactive values after initialisation. Not only is the internal object structure quite complicated (see below). The methods to access a ReactiveValues object hide the complexity behind a very simple API. And that simple API overwrites the function to change element names: there is an implementation that override the S3 names<- method. The function shiny:::``names<-.reactivevalues`` throws an error without doing anything else.

All in all: even if someone finds a way to circumvent these precautions, it is not recommended to change the names of reactive values on the fly.

Alternative Option

Distinguish between

  1. internal keys to identify objects programmatically and
  2. the displayable labels to show to the users in the UI.

That approach is common practice not only in your case. That way you get stable keys that identify your objects but you can show the users whatever labels they need to understand the App. It also frees you from certain restrictions. An internal has certain restrictions (see ?make.names) that users do not understand and may confuse them.

This approach requires with a second object to map the internal keys to display labels. A simple structure `(key = "displayname", ...) will do it.

Object Structure

Here some more details to explain what it is so hard to access the names . uiTbl is a list of S3 class reactivevalues. It wraps three elements of which one is the R6 ReactiveValues class. And that class does not provide direct access to the values you stored in uiTbl but contains as member .values which is another R6 class of type Map. The data of this class is private (i.e. cannot be accessed from outside). There is not function that would allow you to change the names of the elements.

uiTbl (list as S3 class `reactivevalues`)
├──impl (R6 class `ReactiveValues`)
│  ├── .values (R6 class `Map`)
│  |   ├── map: list (no access because private)
│  |   └── ... further public methods
│  └── ... further public and private members
├──readonly (TRUE/FALSE)
└──ns (name space)
  • Related