Home > database >  R Shiny dealing with a dynamic observers list
R Shiny dealing with a dynamic observers list

Time:07-19

I am reading a dataframe.

Then from this dataframe I am dynamically creating a column where I am rendering checkboxes that will be printed along with the rest of the table in Shiny DT (in other cases buttons, textinput, but I'll make the example with checkboxes).

Some checkboxes will be rendered and disabled at startup.

Some checkboxes will be rendered and I will need to observe them, so I can write the changes to the original dataframe (the idea is that there will be a button to export the edited table).

The problem is that the list of checkbox is of course dynamic and therefore stored in a reactive environment, and to the observeEvent function I can only pass one element at a time.

In the example below it probably doesn't make sense to use reactive on the table, but it makes sense in the broader scope of this, I just tried to make the example as easy as possible.

As in the code below

Question1: How to iterate across the rows of the table, and disabling the checkboxes with shinyJS if the column mytype equals DISABLED, this might sound as an easy fix, but the iteration never works, googling my way out it seems that I need to use the lapply function, but I am failing to implement it properly, where am I wrong?

Question2: How can I trigger this first part only at startup?

Question3: this is the second part, now I want to listen to all my checkboxes when they change (ticked or unticked), in order to write this information to the original dataframe. printing the input list I can see all my reactivalues since I am unbinding/binding with the callbacks, but, again, I am failing to iterate on this part.

Any hint is much appreciated. this is driving me insane.

library("DT")
library("shinyjs")
library("tidyverse")
library("shiny")

ui <- basicPage(
  h2("The mtcars data"),
  useShinyjs(),
  DT::dataTableOutput("mytable")
)

server <- function(input, output) {
  
  shinyInput <- function(FUN, len, id, ...) {
    inputs <- as.character(FUN(paste0(id,'_', len), ...))
    inputs
  }

  mtcarsx <- reactive(data.frame(mtcars) %>%
    as_tibble() %>%
    mutate( randmz=runif(row_number(),min=0,max=3)) %>%
    mutate(rown=row_number()) %>%
    rowwise() %>% 
    mutate( 
      newvar=case_when(
        #I have checkboxes defaulted to true, checkboxes defaulted to false, checkboxes defaulted to true that I will disable
        randmz < 1 ~ as.character(checkboxInput(paste0("chk",rown),label="",value=TRUE)),
        randmz < 2 ~ as.character(checkboxInput(paste0("chk",rown),label="",value=FALSE)),        
        TRUE ~ as.character(checkboxInput(paste0("chk",rown),label="",value=TRUE))          
      ),
      mytype=case_when(
        #I have checkboxes defaulted to true, checkboxes defaulted to false, checkboxes defaulted to true that I will disable
        randmz < 1 ~ "MYTRUE",
        randmz < 2 ~ "MYFALSE",        
        TRUE ~ "DISABLED"          
      )
    )
  )
  
  #now I want to disable the ones falling in the third category
  #question1: how to iterate on them?
  #question2: how to trigger this?

  #observe(
    #lapply(mtcarsx()$mytype,function(i){if(mtcarsx()$mytype[[i]]=="DISABLED"){  shinyjs::disable(paste0("chk",i)) }   } )
    #here some more:
    #myres <- lapply(mtcarsx()$mytype,function(i){  shinyjs::disable(paste0("chk",i))    } )
    #mtcarsx()$disable <- lapply(mtcarsx()$mytype,function(i){  observe(shinyjs::disable(paste0("chk",i)))    } )      
    #mtcarsx()$disable <- lapply(mtcarsx()$mytype,function(i){if(mtcarsx()$mytype[[i]]=="DISABLED"){ observe(shinyjs::disable(paste0("chk",i))) }   } )   
    #for (mychk in 1:nrow(mtcarsx()){
    #  if(mtcarsx()$mytype=="DISABLED"){
    #    shinyjs::disable( paste0("chk",mychk)  )
    #    print(paste0("chk",mychk))
    #  }
    #}
  
  #) 
  
  
  #here I am listening to all the checkboxes when they change state, regardless of their default value or enabled/disabled state
  #because I want to write the change to the original dataframe
  #Question3: how to iterate here and listen to any change in a list of reactval? I can pass only one element at a time to input as it is reactive

  
  
 #print(mtcarsx)
  
  output$mytable = DT::renderDataTable({
    DT::datatable(mtcarsx(), 
                  escape = FALSE, 
                  selection = 'none', 
                  rownames = TRUE, 
                  extensions = c('FixedColumns'),
                  options = list(searching = FALSE, 
                                 ordering  = FALSE,
                                 autoWidth = TRUE,
                                 scrollX = TRUE,
                                 FixedColumns = list(leftColumns = c(2)),
                                 preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
                                 drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')
                  ))
  })
  
}

shinyApp(ui, server) 

CodePudding user response:

Try this

  #now I want to disable the ones falling in the third category
  #question1: how to iterate on them?
  #question2: how to trigger this?
  
  observe({
    print(input$chk1)
    n <- nrow(mtcarsx())
    lapply(1:n, function(i){if(mtcarsx()$mytype[i]=="DISABLED"){  shinyjs::disable(paste0("chk",i)) } })
  })

output

  • Related