Using eventReactive with a rendering function - r

I've been stuck on this problem for two days now, and I would love some help from people much smarter than me. I am using a package called "shinyTable"(https://github.com/trestletech/shinyTable), and I am having a hard time manipulating it. Basically, how can I make this table change its size based on input$rows IF I click on the "submit" button?Here is a working code w/o the "submit" button:
library(shinythemes)
library(shiny)
library(shinyTable)
ui <- fluidPage(theme = shinytheme("slate"),titlePanel(HTML("<h1> <font face=\"Rockwell Extra Bold\" color=\"#b42000\"><b><b>R/Econ</b></b></font> <font face=\"Lucida Calligraphy\" colsor=\"white\" >Model</font></h1>")),
sidebarLayout(
sidebarPanel(
numericInput("rows", label = h3("Number of Rows"), value = 20),
numericInput("cols", label = h3("Number of Columns"), value = 2)
),
mainPanel(
htable("tbl")
)
)
)
server <- function(input, output)
{
output$tbl <- renderHtable({
if (is.null(input$tbl)){
# Seed the element with some data initially
tbl <- data.frame(list(num1=1:input$rows,
num2=(1:input$rows)*20,
letter=LETTERS[1:(input$rows)]))
cachedTbl <<- tbl
print(tbl)
return(tbl)
} else{
cachedTbl <<- input$tbl
print(input$tbl)
return(input$tbl)
}
})
}
shinyApp(ui = ui, server = server)
Now, I want the table's size to change dynamically when my input$rows or input$cols changes. I cannot for the life of me figure out how to make this work. I tried the following:
myx<-eventReactive (input$submit, {
output$tbl <- renderHtable({
if (is.null(input$tbl)){
tbl <- data.frame(list(num1=1:input$rows,
num2=(1:input$rows)*20,
letter=LETTERS[1:(input$rows)]))
cachedTbl <<- tbl
print(tbl)
return(tbl)
} else{
cachedTbl <<- input$tbl
print(input$tbl)
return(input$tbl)
}
})
})
But this doesn't work. My thought process was that if the submit button is clicked, it would recreate the table. I want input$rows to change the size of the table, but neither my changing the size nor my clicking on a submit button does anything. In fact, adding eventReactive changes the table to where it has no values, and no values can be inputted. I'm honestly lost. I tried other variations of this such as this:
myx<-eventReactive (input$submit, {
if (is.null(input$tbl)){
tbl <- data.frame(list(num1=1:input$rows,
num2=(1:input$rows)*20,
letter=LETTERS[1:(input$rows)]))
cachedTbl <<- tbl
print(tbl)
return(tbl)
} else{
cachedTbl <<- input$tbl
print(input$tbl)
}
})
#-------
# myx2<-eventReactive (cachedTbl, {
# })
output$tbl <- renderHtable({
tbl<<-myx()
print(data.frame(tbl))#Tried and failed using myx()
return(data.frame(tbl))
})
In doing this, I thought I can make the table reactive and then pass it to renderHTable. All these attempts share the fact that I'm trying to make things reactive.
How can I make this table change its size based on input$rows IF I click on the "submit" button? Please help!

This should get you started. As per my comment, you should use rhandsontable. This package uses the same underlying JS library, handsontable.JS, but it is well supported and it is on Cran (disclaimer: I'm a minor contributor to this package).
The working example below is based on rhandsontable. For simplicity I've only implemented the change of the number of rows.
Please take into account that I haven't implemented any type of caching mechanism, either to a global variable, or to a reactive variable, as it wasn't necessary, but it can easily be added.
This is the only example that I know of a library working in shiny where there is an output$something linked to an input$something.
In this case the input$tbl in the code refer to the table, but to be converted to a data frame it needs to be transformed by the convenience function hot_to_r (handsontable to R).
I am sure you are already familiar with this: you use hot_to_r(input$tbl) to check if the user has changed anything in the displayed table (assuming it is not read-only). shinyTable has a much more complicated mechanism, but it is prone to races.
library(shinythemes)
library(shiny)
library(rhandsontable)
ui <- fluidPage(theme = shinytheme("slate"),titlePanel(HTML("<h1> <font face=\"Rockwell Extra Bold\" color=\"#b42000\"><b><b>R/Econ</b></b></font> <font face=\"Lucida Calligraphy\" colsor=\"white\" >Model</font></h1>")),
sidebarLayout(
sidebarPanel(
numericInput("rows", label = h3("Number of Rows"), value = 20),
numericInput("cols", label = h3("Number of Columns"), value = 2)
),
mainPanel(
rHandsontableOutput("tbl")
)
)
)
server <- function(input, output, session) {
data = reactive({
if (is.null(input$tbl)) {
DF <- data.frame(num1 = 1:input$rows, bool = TRUE, nm = LETTERS[1:input$rows],
dt = seq(from = Sys.Date(), by = "days", length.out = input$rows),
stringsAsFactors = F)
} else if(nrow(hot_to_r(input$tbl)) == input$rows) {
DF <- hot_to_r(input$tbl)
} else {
DF <- data.frame(num1 = 1:input$rows, bool = TRUE, nm = LETTERS[1:input$rows],
dt = seq(from = Sys.Date(), by = "days", length.out = input$rows),
stringsAsFactors = F)
}
DF
})
output$tbl <- renderRHandsontable({
if (is.null(input$rows) | is.null(input$cols)) return()
df = data()
if (!is.null(df))
rhandsontable(df, stretchH = "all")
})
}
shinyApp(ui = ui, server = server)
Please let me know if this works for you, else I'll do my best to change it as per your needs.

Related

Rshiny : Add a free text for comments

I have a data coming from a server. Now I want to add a free text column ( editable) to add comments to my R shiny application. Once that is done , I want to save it in SQLLite and bring it back once it is refreshed. Please help me with the pointers.
library(shiny)
library(ggplot2) # for the diamonds dataset
ui <- fluidPage(
title = "Examples of DataTables",
sidebarLayout(
sidebarPanel(
conditionalPanel(
'input.dataset === "diamonds"'
)
),
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("diamonds", DT::dataTableOutput("mytable1"))
)
)
)
)
library(DT)
server <- function(input, output) {
# choose columns to display
diamonds2 = diamonds[sample(nrow(diamonds), 1000), ]
diamonds2$test <- ifelse(diamonds2$x > diamonds2$y,TRUE,FALSE)
output$mytable1 <- DT::renderDataTable({
DT::datatable(diamonds2[, drop = FALSE],extensions = 'FixedColumns',options = list(
dom = 't',
scrollX = TRUE,
fixedColumns = list(leftColumns =10)
)) %>%
formatStyle(
'x', 'test',
backgroundColor = styleEqual(c(TRUE, FALSE), c('gray', 'yellow'))
)
})
}
Please guide how can I add free text in the end of the table and save it.
Thanks in advance.
Regards,
R
Here is a solution based on DTs editable option. (See this for more information)
Each time the user edits a cell in the "comment" column it is saved to a sqlite database and loaded again after restarting the app:
library(shiny)
library(DT)
library(ggplot2) # diamonds dataset
library(RSQLite)
library(DBI)
# choose columns to display
diamonds2 = diamonds[sample(nrow(diamonds), 1000),]
diamonds2$test <- ifelse(diamonds2$x > diamonds2$y, TRUE, FALSE)
diamonds2$id <- seq_len(nrow(diamonds2))
diamonds2$comment <- NA_character_
con <- dbConnect(RSQLite::SQLite(), "diamonds.db")
if(!"diamonds" %in% dbListTables(con)){
dbWriteTable(con, "diamonds", diamonds2)
}
ui <- fluidPage(title = "Examples of DataTables",
sidebarLayout(sidebarPanel(
conditionalPanel('input.dataset === "diamonds"')
),
mainPanel(tabsetPanel(
id = 'dataset',
tabPanel("diamonds", DT::dataTableOutput("mytable1"))
))))
server <- function(input, output, session) {
# use sqlInterpolate() for production app
# https://shiny.rstudio.com/articles/sql-injections.html
dbDiamonds <- dbGetQuery(con, "SELECT * FROM diamonds;")
output$mytable1 <- DT::renderDataTable({
DT::datatable(
dbDiamonds,
# extensions = 'FixedColumns',
options = list(
dom = 't',
scrollX = TRUE
# , fixedColumns = list(leftColumns = 10)
),
editable = TRUE,
# editable = list(target = "column", disable = list(columns = which(names(diamonds2) %in% setdiff(names(diamonds2), "comment"))))
) %>% formatStyle('x', 'test', backgroundColor = styleEqual(c(TRUE, FALSE), c('gray', 'yellow')))
})
observeEvent(input$mytable1_cell_edit, {
if(input$mytable1_cell_edit$col == which(names(dbDiamonds) == "comment")){
dbExecute(con, sprintf("UPDATE diamonds SET comment = '%s' WHERE id = %s", input$mytable1_cell_edit$value, input$mytable1_cell_edit$row))
}
})
}
shinyApp(ui, server, onStart = function() {
onStop(function() {
dbDisconnect(con) # close connection on app stop
})
})
Initially I wanted to disable editing for all columns except "comment", however, it seems I've found a bug.
The following example adds a <input type="text"> element to each row of the table, where you can add your free text. A simple JavaScript event listener reacts on changes to the text boxes and stores them in the Shiny variable free_text which you can then process on the shiny side according to your needs (in this toy example it is simply output to a verbatimTextOutput).
As for the storing: I would add a save button, which reads input$free_text and saves it back to the data base. To display the text then again in the text boxes is as easy as adding the value in the mutate statement like this mutate(free_text = sprintf("<input type=\"text\" class = \"free-text\" value = \"%s\" />", free_text_field_name))
library(shiny)
library(DT)
library(dplyr)
ui <- fluidPage(
tags$head(
tags$script(
HTML(
"$(function() {
// input event fires for every change, consider maybe a debounce
// or the 'change' event (then it is only triggered if the text box
// loses focus)
$('#tab').on('input', function() {
const inputs = $(this).find('.free-text').map(function() {
return this.value;
})
Shiny.setInputValue('free_text', inputs.get());
})
})
"
)
)
),
fluidRow(
verbatimTextOutput("out")
),
fluidRow(
dataTableOutput("tab")
)
)
server <- function(input, output, session) {
output$tab <- renderDataTable({
my_dat <- mtcars %>%
mutate(free_text =
sprintf("<input type=\"text\" class = \"free-text\" value = \"\" />"))
datatable(my_dat, escape = FALSE,
options = list(dom = "t", pageLength = nrow(mtcars)))
})
output$out <- renderPrint(input$free_text)
}
shinyApp(ui, server)
You may want to have a look at the handsontable package, which allows editing of (columns of) datatable outputs. In your case, you can create a character column and allow editing through the handsontable.
On the topic of persisting data: you table would need either a separate column with comments, or a separate table that maps observations to comment, which is joined. The best solution depends on the volume of comments you expect: if you expect comment to appears sporadically, a separate table may be the best solution. If you expect comments for nearly every row, direct integration into the table may be more favourable. It then becomes a matter of writing to and loading from an SQL database based on user events.

How to use bookmark to document the edited contents in a DT table in Shiny?

I have a Shiny app with a Bookmark button and a DT table that allow the users to edit the contents (https://yuchenw.shinyapps.io/DT_Bookmark/). However, it seems like the Bookmark function cannot document the edited contents in the DT table.
Here is an example. I changed the car name in the first row to "Mazda RX4 aaaaa", and then I clicked "Bookmark button". It can generate an URL. But when I copied and pasted the URL to a new browser, it shows the original state of the app.
Is there a way to make the Bookmark function working? Here is the code.
library(shiny)
library(DT)
ui <- fluidPage(
titlePanel("Bookmark DT Example"),
sidebarLayout(
sidebarPanel(
bookmarkButton()
),
mainPanel(
DTOutput(outputId = "mDT")
)
)
)
server <- function(input, output){
rev <- reactiveValues(dat = mtcars)
output$mDT <- renderDT(
mtcars,
rownames = TRUE,
selection = "none",
editable = TRUE
)
dat_proxy <- dataTableProxy("mDT")
observeEvent(input$mDT_cell_edit, {
rev$dat <- editData(rev$dat, input$mDT_cell_edit, dat_proxy)
})
}
shinyApp(ui, server, enableBookmarking = "url")
The last modification to the datatable is registered in input$mDT_cell_edit.
input$mDT_cell_edit is saved in the bookmarked state, and you can use onRestore to restore it.
However, the full data used in the DT isn't saved : you could use onBookmark to save it too.
As this goes over the 2000 characters allowed by an url, you need to store the bookmark on the server with enableBookmarking = "server".
This is what is done in the code below, to show the way to move forward. It would of course be more efficient to save/restore the list of modifications only.
library(shiny)
library(DT)
server <- function(input, output){
rev <- reactiveValues(dat = mtcars)
output$mDT <- renderDT(
rev$dat,
rownames = TRUE,
selection = "none",
editable = TRUE
)
dat_proxy <- dataTableProxy("mDT")
observeEvent(input$mDT_cell_edit, {
info <- input$mDT_cell_edit
i <- info$row
j <- info$col
if (j>0) {
rev$dat[i, j] <<- DT::coerceValue(info$value, rev$dat[i, j])}
else {
row.names(rev$dat)[i] <- info$value
}
DT::replaceData(dat_proxy, rev$dat, resetPaging = FALSE, rownames = T)
})
onBookmark(function(state) {
state$values$rev_dat <- rev$dat
})
# restore table selection and search
onRestored(function(state) {
if (!identical(rev$dat,state$values$rev_dat)) {
rev$dat <- state$values$rev_dat
DT::replaceData(dat_proxy, state$values$rev_dat, resetPaging = FALSE, rownames = T)
}
})
}
shinyApp(ui, server, enableBookmarking = "server")

Using dynamic input from tagList to renderTable in Shiny

What I am trying to do is have the user specify the number of groups then, based on the number of groups specified, the UI generates a numericInput for each group. Then I want to use that value to do some other operations (in this example, I'm making a table of means). Using this example, I was able to make it return some text, but not use that label as input for anything else.
When I try to use that information (i.e., as reactive conductor), I get a "replacement has length zero" error. It seems shiny is not recognizing the updated UI. I know it probably has something to do with using reactive, but I can't figure out why it's not working. Here's my code:
library(shiny)
library(purrr)
# functions ---------------------------------------------------------------
## generic function that creates an input from an i
make_list = function(i, idname, labelname){
idname <- paste(idname, i, sep = "")
div(style="display: inline-block;vertical-align:top; width: 45%;",
numericInput(idname, labelname, 0))
}
## make function that can be used within a loop
list_loop = function(i) {
make_list(i, "mean", "Mean of Group ")
}
# UI ----------------------------------------------------------------------
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("A Test Page"),
sidebarLayout(
sidebarPanel(width = 8,
#### UI for groups
numericInput("groups", "How many groups?", 4),
hr(),
uiOutput("inputMean")),
# Main panel for displaying outputs ----
mainPanel(width = 4,
h3("Data Preview"),
#textOutput("inputValues"),
tableOutput("table"))
)
)
# Server ------------------------------------------------------------------
# Define server logic required to draw a histogram
server = function(input, output) {
## loop through # of groups for all i and make the UI
## this is passed back to the UI
observeEvent(input$groups,
{
output$inputMean = renderUI(
{
mean_list <- 1:input$groups %>% map(~list_loop(.x))
do.call(tagList, mean_list)
}
)
}
)
## return the inputnames
## This WORKS
output$inputValues <- renderText({
paste(lapply(1:input$groups, function(i) {
inputName <- paste("mean", i, sep = "")
input[[inputName]]
}))
})
make_table = reactive({
### prepopulate a table
d = data.frame(group = 1:input$groups)
d$means = NA
paste(lapply(1:input$groups, function(i) {
inputName <- paste("mean", i, sep = "")
# this fails because input is NULL at this point
d$means[i] = input[[inputName]]
}))
d
})
output$table <- renderTable({
make_table()
})
}
# Run the application
shinyApp(ui = ui, server = server)
If you replace your make_table with the following, it works.
I added a req that checks if all the input is present, so it won't throw errors anymore. Then, I filled d$means using the lapply you created.
make_table = reactive({
req(input$groups, input[[paste("mean", input$groups, sep = "")]])
### prepopulate a table
d = data.frame(group = 1:input$groups)
d$means = lapply(1:input$groups, function(i) {
inputName <- paste("mean", i, sep = "")
# this fails because input is NULL at this point
input[[inputName]]
})
d
})

Reading objects from shiny output object not allowed?

I'm trying to write a little app that will allow the user to make a scatterplot, select a subset of points on the plot, then output a table in .csv format with just those selected points. I figured out how to get the page up and running and how to select points using brushedPoints. The table with selected points appears but when I press the Download button, the error "Reading objects from shinyoutput object not allowed." appears. Am I unable to download the table that I can visually see on the screen as a .csv? If so, is there a workaround?
I've recreated the problem using the iris dataset below. Any help figuring out why I cannot download the table of displayed rows would be greatly appreciated.
data(iris)
ui <- basicPage(
plotOutput("plot1", brush = "plot_brush"),
verbatimTextOutput("info"),mainPanel(downloadButton('downloadData', 'Download'))
)
server <- function(input, output) {
output$plot1 <- renderPlot({
ggplot(iris,aes(x=Sepal.Width,y=Sepal.Length)) +
geom_point(aes(color=factor(Species))) +
theme_bw()
})
output$info <- renderPrint({
brushedPoints(iris, input$plot_brush, xvar = "Sepal.Width", yvar = "Sepal.Length")
})
output$downloadData <- downloadHandler(
filename = function() {
paste('SelectedRows', '.csv', sep='') },
content = function(file) {
write.csv(output$info, file)
}
)
}
shinyApp(ui, server)
The issue is that the output object is generating all of the web display stuff as well. Instead, you need to pull the data separately for the download. You could do it with a second call to brushedPoints in the download code. Better, however, is to use a reactive() function to do it just once, then call that everywhere that you need it. Here is how I would modify your code to make that work:
data(iris)
ui <- basicPage(
plotOutput("plot1", brush = "plot_brush"),
verbatimTextOutput("info"),mainPanel(downloadButton('downloadData', 'Download'))
)
server <- function(input, output) {
output$plot1 <- renderPlot({
ggplot(iris,aes(x=Sepal.Width,y=Sepal.Length)) + geom_point(aes(color=factor(Species))) + theme_bw()
})
selectedData <- reactive({
brushedPoints(iris, input$plot_brush)
})
output$info <- renderPrint({
selectedData()
})
output$downloadData <- downloadHandler(
filename = function() {
paste('SelectedRows', '.csv', sep='') },
content = function(file) {
write.csv(selectedData(), file)
}
)
}
shinyApp(ui, server)
(Note, with ggplot2, you do not need to explicitly set xvar and yvar in brushedPoints. So, I removed it here to increase the flexibility of the code.)
I am not aware of any "lasso" style free drawing ability in shiny (though, give it a week -- they are constantly adding fun tools). However, you can mimic the behavior by allowing user to select multiple regions and/or to click on individual points. The server logic gets a lot messier, as you need to store the results in a reactiveValues object to be able to use it repeatedly. I have done something similar to allow me to select points on one plot and highlight/remove them on other plots. That is more complicated than what you need here, but the below should work. You may want to add other buttons/logic (e.g., to "reset" the selections), but I believe that this should work. I did add a display of the selection to the plot to allow you to keep track of what has been selected.
data(iris)
ui <- basicPage(
plotOutput("plot1", brush = "plot_brush", click = "plot_click")
, actionButton("toggle", "Toggle Seletion")
, verbatimTextOutput("info")
, mainPanel(downloadButton('downloadData', 'Download'))
)
server <- function(input, output) {
output$plot1 <- renderPlot({
ggplot(withSelected()
, aes(x=Sepal.Width
, y=Sepal.Length
, color=factor(Species)
, shape = Selected)) +
geom_point() +
scale_shape_manual(
values = c("FALSE" = 19
, "TRUE" = 4)
, labels = c("No", "Yes")
, name = "Is Selected?"
) +
theme_bw()
})
# Make a reactive value -- you can set these within other functions
vals <- reactiveValues(
isClicked = rep(FALSE, nrow(iris))
)
# Add a column to the data to ease plotting
# This is really only necessary if you want to show the selected points on the plot
withSelected <- reactive({
data.frame(iris
, Selected = vals$isClicked)
})
# Watch for clicks
observeEvent(input$plot_click, {
res <- nearPoints(withSelected()
, input$plot_click
, allRows = TRUE)
vals$isClicked <-
xor(vals$isClicked
, res$selected_)
})
# Watch for toggle button clicks
observeEvent(input$toggle, {
res <- brushedPoints(withSelected()
, input$plot_brush
, allRows = TRUE)
vals$isClicked <-
xor(vals$isClicked
, res$selected_)
})
# pull the data selection here
selectedData <- reactive({
iris[vals$isClicked, ]
})
output$info <- renderPrint({
selectedData()
})
output$downloadData <- downloadHandler(
filename = function() {
paste('SelectedRows', '.csv', sep='') },
content = function(file) {
write.csv(selectedData(), file)
}
)
}
shinyApp(ui, server)

How to save edits made using rhandsontable r package

My R program works as expected. It shows a table containing my dataFrame, and lets me edit the values.
How do I capture those values and save them to my dataframe, or a copy of my dataframe?
require(shiny)
library(rhandsontable)
DF = data.frame(val = 1:10, bool = TRUE, big = LETTERS[1:10],
small = letters[1:10],
dt = seq(from = Sys.Date(), by = "days", length.out = 10),
stringsAsFactors = F)
rhandsontable(DF, rowHeaders = NULL)
EDIT:
The above code produces a table with rows and columns. I can edit any of the rows and columns. But when I look at my dataFrame, those edits do not appear. What I am trying to figure out is what do I need to change so I can capture the new values that were edited.
I know this thread's been dead for years, but it's the first StackOverflow result on this problem.
With the help of this post - https://cxbonilla.github.io/2017-03-04-rhot-csv-edit/, I've come up with this:
library(shiny)
library(rhandsontable)
values <- list()
setHot <- function(x)
values[["hot"]] <<- x
DF <- data.frame(val = 1:10, bool = TRUE, big = LETTERS[1:10],
small = letters[1:10],
dt = seq(from = Sys.Date(), by = "days", length.out = 10),
stringsAsFactors = FALSE)
ui <- fluidPage(
rHandsontableOutput("hot"),
br(),
actionButton("saveBtn", "Save changes")
)
server <- function(input, output, session) {
observe({
input$saveBtn # update dataframe file each time the button is pressed
if (!is.null(values[["hot"]])) { # if there's a table input
DF <<- values$hot
}
})
observe({
if (!is.null(input$hot)){
DF <- (hot_to_r(input$hot))
setHot(DF)
}
})
output$hot <- renderRHandsontable({
rhandsontable(DF) %>% # actual rhandsontable object
hot_table(highlightCol = TRUE, highlightRow = TRUE, readOnly = TRUE) %>%
hot_col("big", readOnly = FALSE) %>%
hot_col("small", readOnly = FALSE)
})
}
shinyApp(ui = ui, server = server)
However, I don't like my solution on the part of DF <<- values$hot as I previously had problems with saving changes to the global environment. I've couldn't figure it out any other way, though.
It seems to be accessible now via input$NAME_OF_rHandsontableOutput and can be converted to a data.frame via hot_to_r().
Reproducible example:
library(shiny)
library(rhandsontable)
ui <- fluidPage(
rHandsontableOutput("hottable")
)
server <- function(input, output, session) {
observe({
print(hot_to_r(input$hottable))
})
output$hottable <- renderRHandsontable({
rhandsontable(mtcars)
})
}
shinyApp(ui, server)
I was able to accomplish this with a more simple solution for saving data while the app is open and after it is closed for shiny 1.7++
Create an observe event dependent upon a save button clicked at any point when the app is open. I've scaled this method in more complex apps where you have a selectizeinput for swapping in and out different data frames into the rhandsontable, each of which are edited, saved and recalled while the app is open.
In the server:
observeEvent(input$save, { #button is the name of the save button, change as needed
df <<- hot_to_r(input$rhandsontable) #replace rhandsontable with the name of your own
}) #df is the data frame that have it access when the app starts
In the UI:
actionButton("save","Save Edits")
I don't know what you want to recover exactly, but this seems to work:
DF <- rhandsontable(DF, rowHeaders = NULL)
library(jsonlite)
fromJSON(DF$x$data)
If you are using Shiny then input$table$changes$changes can give you the edited value with row and column index. Below is the code if you want to update only specific cell and not the complete table using hot_to_t().
library(shiny)
library(rhandsontable)
DF = data.frame(val = 1:10, bool = TRUE, big = LETTERS[1:10],
small = letters[1:10],
dt = seq(from = Sys.Date(), by = "days", length.out = 10),
stringsAsFactors = F)
ui <- fluidPage(
rHandsontableOutput('table')
)
server <- function(input, output) {
X = reactiveValues(data = DF)
output$table <- rhandsontable::renderRHandsontable({
rhandsontable(X$data, rowHeaders = NULL)
})
observeEvent(input$table$changes$changes,{
row = input$table$changes$changes[[1]][[1]]
col = input$table$changes$changes[[1]][[2]]
value = input$table$changes$changes[[1]][[4]]
X$data[row,col] = value
})
}
shinyApp(ui, server)
Here's an example from related post How to add columns to a data frame rendered with rhandsontable in R Shiny with an action button?, which started with Tonio Liebrand's solution above but rendered reactively with columns added by the user via action button so you can see the table evolve and see how manual edits to the table stick around:
library(shiny)
library(rhandsontable)
myDF <- data.frame(x = c(1, 2, 3))
ui <- fluidPage(rHandsontableOutput('hottable'),
br(),
actionButton('addCol', 'Add'))
server <- function(input, output, session) {
EmptyTbl <- reactiveVal(myDF)
observeEvent(input$hottable, {
EmptyTbl(hot_to_r(input$hottable))
})
output$hottable <- renderRHandsontable({
rhandsontable(EmptyTbl())
})
observeEvent(input$addCol, {
newCol <- data.frame(c(1, 2, 3))
names(newCol) <- paste("Col", ncol(hot_to_r(input$hottable)) + 1)
EmptyTbl(cbind(EmptyTbl(), newCol))
})
}
shinyApp(ui, server)

Resources