User Input is not displayed and not editable after adding columns

Dear R-community,

I am trying to build an app with R shiny. I am using selectInput() and textInput() in cells of a table. Moreover, I want the user to be able to click a button to add a column in the table with those input fields. the problem is, that when I add a column, the entered values in the first column are saved, but not displayed, and if I re-enter some values in the first column, it doesn't change the already saved values.

I built a mock example to demonstrate my problem:

#load data
data <- data.frame(c = sample(c("A", "B"), 300, replace = TRUE), v = sample(c("w", "x"), 300, replace = TRUE), s = sample(c("FF", "GG", "HH"), 300, replace = TRUE))
data <- data[!duplicated(data),]
data <- data[order(data$c, data$v, data$s),]

numb_of_c <- length(unique(data$c))


ui <- dashboardPage(
  dashboardHeader(title = "TEST",
                  titleWidth = 450), 
  dashboardSidebar(
    width = 450,
    sidebarMenu(
      lapply(1:numb_of_c, function(i) {
        menuItem(unique(data$c)[i], tabName = unique(data$c)[i])
      })
    )),
  dashboardBody(
    box(actionButton("add_col", label = "Add column"),
        actionButton("rm_col", label = "Remove column"),
        verbatimTextOutput("printtext")),
    uiOutput("tab_items_output")
    )
)


server <- function(input, output, session) {

  #counter for number of columns
  counter <- reactiveValues(n = 0)
  observeEvent(input$add_col, {counter$n <- counter$n + 1})
  observeEvent(input$rm_col, {
    if (counter$n > 0) counter$n <- counter$n - 1
  })
  
  #ui for all c
  output$tab_items_output <- renderUI({
    theitems <- list()
    for (i in 1:numb_of_c) {
      theitems[[unique(data$c)[i]]] <- tabItem(tabName = unique(data$c)[i],
                                                       box(width = 12,
                                                           uiOutput(outputId = paste0("ui_", unique(data$c)[i]))
                                                       )
      )
    }
    
    theitems <- unname(theitems)
    do.call(tabItems, theitems)
  })
  
  #ui for each c
  lapply(1:numb_of_c, function(i) {
    gen_cols <- reactive({
      #get data frame of current c
      temp <- data[data$c==unique(data$c)[i],]
      # Create selectInput and textInput objects to embed in table
      table_inputs <- character(nrow(temp))
      for (y in 1:(counter$n+1)) {
        for (x in 1:nrow(temp)) {
          if (temp$s[x]=="HH") { #if categorical
            table_inputs[x] <- as.character(selectInput(paste0(as.character(temp$c[x]), as.character(temp$v[x]), as.character(temp$s[x]), y), label = NULL, choices = 1:5))
          }
          else {
            table_inputs[x] <- as.character(textInput(paste0(temp$c[x], temp$v[x], temp$s[x], y), label = NULL, value = NULL)) 
          }
        }
        temp <- cbind(temp, table_inputs)
      }
      temp
    })
    
    #create ui of table
    output[[paste0("ui_", unique(data$c)[i])]] <- renderUI({
      box(width=12,
          fluidRow(dataTableOutput(outputId = paste0("table_", gen_cols()$c[i]))
          )
      )
    })
    #table ui
    output[[paste0("table_", data[data$c==unique(data$c)[i],"c"][i])]] <- DT::renderDataTable(
      gen_cols(),
      selection = 'none',
      server = FALSE,
      escape = FALSE,
      rownames = FALSE,
      options = list(
        scrollX = TRUE,
        paging = TRUE,
        pageLength = 100,
        lengthMenu = c(5, 10, 20, 100),
        preDrawCallback = JS('function() { 
                             Shiny.unbindAll(this.api().table().node()); }'), 
        drawCallback = JS('function() { 
                          Shiny.bindAll(this.api().table().node()); } '))
        )
    })
  
  output$printtext <- renderPrint(print(input[["AwFF1"]]))
}

shinyApp(ui, server)

When I am entering a value of 1, I can easily access it as can be seen in the verbatimTextOutput:

When I then add a column, the 1 is still saved in the first textInput, but the textInput field is empty:

And if I try to change the value, it doesn't seem to accept any edits.

I tried to solve it somehow by including:

AllInputs <- reactive({
      x <- reactiveValuesToList(input)
    })

and then setting the value in the input fields, but the app kept refreshing itself:

table_inputs[x] <- as.character(textInput(paste0(temp$c[x], temp$v[x], temp$s[x], y), label = NULL, value = AllInputs()[[paste0(temp$c[x], temp$v[x], temp$s[x], y)]]))

Any help would be much appreciated!

Hi,

I think it's much easier if you use the editable datatable from the DT package. I don't know for sure if that will satisfy your needs, but it certainly gives an easy way to edit a table on the go. You can simply double click any cell in the table to edit it.

Here is an example:

library(dplyr)
library(shiny)
library(DT)

ui <- fluidPage(
  
  headerPanel("Editable table app"),
  actionButton("addColumn", "Add Column"),
  DTOutput("myTable")
  
)

server <- function(input, output, session) {
  
  #Original data
  myData = reactiveVal(data.frame(x = 1:10, y = round(runif(10),2), 
                                       z = LETTERS[1:10], stringsAsFactors = FALSE))
  
  #Data table output
  output$myTable = renderDT({
    datatable(myData(), editable = T, selection = "single")
  })
  
  #Code when column is added
  observeEvent(input$addColumn, {
    #Add a column name on the go (based on number of times button is clicked)
    myData(myData() %>% mutate(!!(paste0("newColumn", input$addColumn)) := 0))
  }, ignoreInit = T)
  
  #Code to update server-side data when edited on client side
  observeEvent(input$myTable_cell_edit, {
    editCell = input$myTable_cell_edit
    
    #In order to update the reactive data, make it local first
    myData = myData()
    #...then update
    myData[editCell$row, editCell$col] = editCell$value
    #...then update the reactive again
    myData(myData)
    
  }, ignoreInit = T)
  
}

shinyApp(ui, server)

Hope this helps,
PJ

1 Like

Hey PJ,

thank you for your reply. I added stringsAsFactors = FALSE to the generation of myData, because otherwise it won't properly save changes if you enter 'invalid factor levels' and instead generates NA values.

Cheers!

Hi,

That's good to know, I updated the stringsAsFactors in the example!

PJ

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.