How to add a nested list to a reactiveValue from a Shiny Module

45 views Asked by At

I need to add data from a nested list from a Shiny module to a global reactiveValues() variable.

The objective is to save different versions of the selected data into the reactiveValues for later use.

library(shiny)
library(tidyverse)
library(shinyWidgets)
library(DT)

addDataUI <- function(id) {
  ns <- NS(id)
  
  tagList(
    fluidRow(
      column(4,
             pickerInput(inputId = ns("measurement_type"),
                         label = "Estimate Type",
                         choices = c("height", "weight"),
                         selected = c("height", "weight"),
                         multiple = TRUE)
             )
    ),
    tableOutput(ns("summary")),
    br(),
    actionButton(ns("add_dataset"), 'Add dataset')
    )
  }



addDataServer <- function(id, dataset) {
  moduleServer(id, function(input, output, session) {
    results <- reactive({
      dataset() %>% 
        filter(measurement_type %in% input$measurement_type) 
    })
    output$summary <- renderTable(results())
    
    addObject <- reactiveVal()
    
    observeEvent(input$add_dataset, {
      objectChoice <- "measurementTable"
      chars <- c(0:9, letters, LETTERS)
      randomId <- stringr::str_c(sample(chars, 4, replace = TRUE) , collapse = "" )
      dataReport <- list()
      addObject(
        dataReport[[randomId]][[objectChoice]] <- dataset()
        )
    })
    addObject
    })
  }


ui <- fluidPage(
  titlePanel("Measurement App"),
  fluidRow(column(6, actionButton("resetDataset", "Reset Dataset"))),
  fluidRow(column(6, addDataUI("measurements"))),
  fluidRow(column(6, DTOutput("dataReportMenu")))
)

server <- function(input, output) {
  
  dataset <- tribble(
    ~database, ~measurement_type, ~estimate,
    "A", "weight", 10,
    "B", "height", 20,
    )
  
  dataReport <- reactiveValues()
  
  observeEvent(input$resetDataset, {
    dataReport <- reactiveValues()
  })
  
  newData <- addDataServer(id = "measurements", dataset = reactive(dataset))
  
  observe({
    for (key in names(newData())) {
      dataReport <- newData()
    }
  }) %>%
    bindEvent(newData())
  
  objectsListPreview <- reactive({
    dataReportList <- reactiveValuesToList(dataReport)
    if (length(dataReportList) == 0) {
      return("None")
    } else {
      objectList <- list()
      for (i in seq(1:length(dataReportList))) {
        objectList <- rbind(objectList, names(dataReportList[[i]]))
      }
      result <- unlist(objectList)
      return(result)
    }
  })
  
  output$dataReportMenu <- renderDT({
    dataReportFrame <- data.frame(
      Name = objectsListPreview()
    )
    DT::datatable(dataReportFrame, options = list(dom = 't'))
  })
  
}

shinyApp(ui = ui, server = server)

I am using this previous example in which they can update a dataframe saved into the reactive value.

But when I want to add a nested list to the reactiveValue, nothing happens in my case.

The dataReportMenu should show only the objectChoice that I am adding as a level in the nested list after the randomId.

1

There are 1 answers

0
YBS On BEST ANSWER

For assignment to reactiveValues, you can use

 for (key in names(newData())) {
      dataReport[[key]] <- newData()
 }

Full code:

library(shiny)
library(tidyverse)
library(shinyWidgets)
library(DT)

addDataUI <- function(id) {
  ns <- NS(id)
  
  tagList(
    fluidRow(
      column(4,
             pickerInput(inputId = ns("measurement_type"),
                         label = "Estimate Type",
                         choices = c("height", "weight"),
                         selected = c("height", "weight"),
                         multiple = TRUE)
      )
    ),
    tableOutput(ns("summary")),
    br(),
    actionButton(ns("add_dataset"), 'Add dataset')
  )
}

addDataServer <- function(id, dataset) {
  moduleServer(id, function(input, output, session) {
    results <- reactive({
      dataset() %>% 
        dplyr::filter(measurement_type %in% input$measurement_type) 
    })
    output$summary <- renderTable(results())
    
    addObject <- reactiveVal()
    
    observeEvent(input$add_dataset, {
      objectChoice <- "measurementTable"
      chars <- c(0:9, letters, LETTERS)
      randomId <- stringr::str_c(sample(chars, 4, replace = TRUE) , collapse = "" )
      dataReport <- list()
      dataReport[[randomId]][[objectChoice]] <- dataset()
      addObject(
        dataReport
      )
      
    })
    return(addObject)
  })
}


ui <- fluidPage(
  titlePanel("Measurement App"),
  fluidRow(column(6, actionButton("resetDataset", "Reset Dataset"))),
  fluidRow(column(6, addDataUI("measurements"))),
  fluidRow(column(6, DTOutput("dataReportMenu")))
)

server <- function(input, output) {
  
  dataset <- tribble(
    ~database, ~measurement_type, ~estimate,
    "A", "weight", 10,
    "B", "height", 20,
  )
  
  dataReport <- reactiveValues()
  
  observeEvent(input$resetDataset, {
    dataReport <- reactiveValues()
  })
  
  newData <- addDataServer(id = "measurements", dataset = reactive(dataset))
  
  observe({
    for (key in names(newData())) {
      dataReport[[key]] <- newData()
    }
  }) %>%
    bindEvent(newData())
  
  # observe({print(newData())})
  
  objectList <- list()
  objectsListPreview <- reactive({
    dataReportList <- reactiveValuesToList(dataReport)
    
    if (length(dataReportList) == 0) {
      return("None")
    } else {
      # objectList <- list()
      for (i in seq(1:length(dataReportList))) {
        objectList <- rbind(objectList, names(dataReportList[[i]]))
      }
      result <- unlist(objectList)
      return(result)
    }
  })
  
  output$dataReportMenu <- renderDT({
    dataReportFrame <- data.frame(
      Name = objectsListPreview()
    )
    DT::datatable(dataReportFrame, options = list(dom = 't'))
  })
  
}

shinyApp(ui = ui, server = server)