Reputation: 314
I am working on a R shiny app that reads CSV and produces a dataTable. I am looking for a way to undo prior actions one by one whenever I clik the Undo button (like CTRL+ Z in Windows), however, the code below restores all previous actions once I press the Undo button.
Could someone please assist me in resolving this problem?
csv data
ID Type Range
21 A1 B1 100
22 C1 D1 200
23 E1 F1 300
app.R
library(shiny)
library(reshape2)
library(DT)
library(tibble)
###function for deleting the rows
splitColumn <- function(data, column_name) {
newColNames <- c("Unmerged_type1", "Unmerged_type2")
newCols <- colsplit(data[[column_name]], " ", newColNames)
after_merge <- cbind(data, newCols)
after_merge[[column_name]] <- NULL
after_merge
}
###_______________________________________________
### function for inserting a new column
fillvalues <- function(data, values, columName){
df_fill <- data
vec <- strsplit(values, ",")[[1]]
df_fill <- tibble::add_column(df_fill, newcolumn = vec, .after = columName)
df_fill
}
##function for removing the colum
removecolumn <- function(df, nameofthecolumn){
df[ , -which(names(df) %in% nameofthecolumn)]
}
### use a_splitme.csv for testing this program
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File", accept = ".csv"),
checkboxInput("header", "Header", TRUE),
actionButton("Splitcolumn", "SplitColumn"),
uiOutput("selectUI"),
actionButton("deleteRows", "Delete Rows"),
textInput("textbox", label="Input the value to replace:"),
actionButton("replacevalues", label = 'Replace values'),
actionButton("removecolumn", "Remove Column"),
actionButton("Undo", 'Undo')
),
mainPanel(
DTOutput("table1")
)
)
)
server <- function(session, input, output) {
rv <- reactiveValues(data = NULL, orig=NULL)
observeEvent(input$file1, {
file <- input$file1
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
rv$orig <- read.csv(file$datapath, header = input$header)
rv$data <- rv$orig
})
output$selectUI<-renderUI({
req(rv$data)
selectInput(inputId='selectcolumn', label='select column', choices = names(rv$data))
})
observeEvent(input$Splitcolumn, {
rv$data <- splitColumn(rv$data, input$selectcolumn)
})
observeEvent(input$deleteRows,{
if (!is.null(input$table1_rows_selected)) {
rv$data <- rv$data[-as.numeric(input$table1_rows_selected),]
}
})
output$table1 <- renderDT({
rv$data
})
observeEvent(input$replacevalues, {
rv$data <- fillvalues(rv$data, input$textbox, input$selectcolumn)
})
observeEvent(input$removecolumn, {
rv$data <- removecolumn(rv$data,input$selectcolumn)
})
observeEvent(input$Undo, {
rv$data <- rv$orig
})
}
Upvotes: 3
Views: 397
Reputation: 7106
We can create a list to host every instance of the table to recover multiple undo's. Note that if the .csv is very big this approach will become inefficient very quick. We can mitigate this infefficiency by implementing a button that clears the undo list up to a point or implementing an append function that saves only the part modified of the table rather than the whole table.
Please, fill free to modify the answer or use it for another answer.
library(shiny)
library(reshape2)
library(DT)
library(tibble)
###function for deleting the rows
splitColumn <- function(data, column_name) {
newColNames <- c("Unmerged_type1", "Unmerged_type2")
newCols <- colsplit(data[[column_name]], " ", newColNames)
after_merge <- cbind(data, newCols)
after_merge[[column_name]] <- NULL
after_merge
}
###_______________________________________________
### function for inserting a new column
fillvalues <- function(data, values, columName){
df_fill <- data
vec <- strsplit(values, ",")[[1]]
tibble::add_column(df_fill, newcolumn = vec, .after = columName)
}
##function for removing the colum
removecolumn <- function(df, nameofthecolumn){
df[ , -which(names(df) %in% nameofthecolumn)]
}
# APP ---------------------------------------------------------------------
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File", accept = ".csv"),
checkboxInput("header", "Header", TRUE),
actionButton("Splitcolumn", "SplitColumn"),
uiOutput("selectUI"),
actionButton("deleteRows", "Delete Rows"),
textInput("textbox", label = "Input the value to replace:"),
actionButton("replacevalues", label = 'Replace values'),
actionButton("removecolumn", "Remove Column"),
actionButton("Undo", 'Undo')
),
mainPanel(
DTOutput("table1")
)
)
)
server <- function(session, input, output) {
#added undo (a list) and counter to accumulate more than one undo
rv <- reactiveValues(data = NULL, orig=NULL, undo = list(), counter = 1)
# csv file ----------------------------------------------------------------
observeEvent(input$file1, {
file <- input$file1
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "csv", "Please upload a csv file"))
rv$orig <- read.csv(file$datapath, header = input$header)
rv$data <- rv$orig
})
output$selectUI <- renderUI({
req(rv$data)
selectInput(inputId='selectcolumn', label='select column', choices = names(rv$data))
})
# rest of the app ---------------------------------------------------------
observeEvent(input$Splitcolumn, {
rv$undo[[rv$counter]] <- rv$data
rv$counter <- rv$counter + 1
rv$data <- splitColumn(rv$data, input$selectcolumn)
})
observeEvent(input$deleteRows,{
if (!is.null(input$table1_rows_selected)) {
rv$undo[[rv$counter]] <- rv$data
rv$counter <- rv$counter + 1
rv$data <- rv$data[-as.numeric(input$table1_rows_selected),]
}
})
output$table1 <- renderDT({
rv$data
})
observeEvent(input$replacevalues, {
rv$undo[[rv$counter]] <- rv$data
rv$counter <- rv$counter + 1
rv$data <- fillvalues(rv$data, input$textbox, input$selectcolumn)
})
observeEvent(input$removecolumn, {
rv$undo[[rv$counter]] <- rv$data
rv$counter <- rv$counter + 1
rv$data <- removecolumn(rv$data,input$selectcolumn)
})
observeEvent(input$Undo, {
if (rv$counter > 1) {
rv$data <- rv$undo[[rv$counter - 1]]
#index must be more than 1
rv$counter <- rv$counter - 1
}
})
}
shinyApp(ui, server)
Upvotes: 2