Preserve row order of rhandsontable in shiny app - r

I am running an example from here.
library(rhandsontable)
library(shiny)
runApp(shinyApp(
ui = fluidPage(rHandsontableOutput("hot")),
server = function(input, output, session) {
fname <- "mtcars2.csv"
values <- reactiveValues()
setHot <- function(x) values[["hot"]] = x
observe({
if(!is.null(values[["hot"]])) write.csv(values[["hot"]], fname)
})
output$hot <- renderRHandsontable({
if (!is.null(input$hot)) {
DF <- hot_to_r(input$hot)
} else {
DF <- read.csv("mtcars.csv", stringsAsFactors = FALSE)
}
setHot(DF)
rhandsontable(DF) %>%
hot_table(highlightCol = TRUE, highlightRow = TRUE) %>%
hot_cols(columnSorting = TRUE)
})
}
))
I want changes made to table be saved in file mtcars2.csv. I also want to preserve row order. In project home page it says "sorting only impacts the widget and will not reorder the original data set". Can I somehow get current view of a table and save it?

The best way to answer this question will be to file an issue at https://github.com/jrowen/rhandsontable. Currently, these lines define only a partial list of handsontable events. This list does not include afterColumnSort which would be what you need. Here is a quick hack to partially answer your question.
library(rhandsontable)
library(shiny)
library(htmlwidgets)
runApp(shinyApp(
ui = fluidPage(
rHandsontableOutput("hot"),
tags$script(
'
setTimeout(
function() {
HTMLWidgets.find("#hot").hot.addHook(
"afterColumnSort",
function(){
console.log("sort",this);
Shiny.onInputChange(
"hot_sort",
{
data: this.getData()
}
)
}
)
},
1000
)
'
)
),
server = function(input, output, session) {
observeEvent(
input$hot_sort
,{
print(input$hot_sort$data)
}
)
output$hot <- renderRHandsontable({
if (!is.null(input$hot)) {
DF <- hot_to_r(input$hot)
} else {
DF <- mtcars
}
rhandsontable(DF) %>%
hot_table(highlightCol = TRUE, highlightRow = TRUE) %>%
hot_cols(columnSorting = TRUE)
})
}
))

I don't think there is a way to preserve the sorted columns in DataTables for shiny, Sad!
With the below code I'm able to save changes made in shiny app to the file mtcars2.csv. Interestingly! post sorting by desired column, clicking on any data cell and pressing enter key saves the row order to the mtcars2.csv. Agree with timelyportolio's point on filing an issue on git.
R Code:
library(shiny)
library(rhandsontable)
runApp(shinyApp(
ui = fluidPage(titlePanel("Edit Data File"),
helpText("Changes to the table will be automatically saved to the source file."),
# actionButton("saveBtn", "Save"),
rHandsontableOutput("hot")),
shinyServer(function(input, output, session) {
values = reactiveValues()
data = reactive({
if (is.null(input$hot)) {
hot = read.csv("mtcars.csv", stringsAsFactors = FALSE)
} else {
hot = hot_to_r(input$hot)
}
# this would be used as a function input
values[["hot"]] = hot
hot
})
observe({
# input$saveBtn
if (!is.null(values[["hot"]])) {
write.csv(values[["hot"]], "mtcars.csv", row.names = FALSE)
}
})
output$hot <- renderRHandsontable({
hot = data()
if (!is.null(hot)) {
hot = rhandsontable(hot) %>%
hot_table(highlightCol = TRUE, highlightRow = TRUE) %>%
hot_cols(columnSorting = TRUE)
hot
}
})
})
))

Related

How to select the particular value in the data table

1.In my app i just want to show the data of the each row separately for Ex if i click any value in the first column the data should show the whole row
like if i click on Mazda RX4 it should show the whole row of the data
2.with the help of extensions = 'ColReorder' i can drag the column inside the table if i drag the first column to others position that clickable should work
for eg if i move the 1st(new_name) column into 4th that clickeable is not working
Any answers would be appreciated
library(shiny)
library(DT)
data("mtcars")
ui <- shinyUI(fluidRow(
DT::dataTableOutput("myDatatable"),
verbatimTextOutput("selectedCells")
))
df <- cbind(new_name =rownames(mtcars), data.frame(mtcars, row.names= NULL))
server <- shinyServer(function(input, output, session) {
output$myDatatable <- DT::renderDataTable(
df, extensions = 'ColReorder', options = list(colReorder = TRUE),selection = list(mode = "single", target ="cell"),
server = FALSE,
rownames = T
)
output$selectedCells <- renderPrint({
s = input$myDatatable_cells_selected
if (!is.null(s) && ncol(s) != 0) {
mtcars[,1]
} else {
NULL
}
})
})
shinyApp(ui, server)
Tested:
library(shiny)
library(DT)
data("mtcars")
ui <- shinyUI(fluidRow(
DT::dataTableOutput("myDatatable"),
verbatimTextOutput("selectedCells")
))
df <- cbind(new_name =rownames(mtcars), data.frame(mtcars, row.names= NULL))
server <- shinyServer(function(input, output, session) {
output$myDatatable <- DT::renderDataTable(
df, extensions = 'ColReorder', options = list(colReorder = TRUE),selection = list(mode = "single", target ="cell"),
server = FALSE,
rownames = T
)
output$selectedCells <- renderPrint({
s_val = input$myDatatable_cell_clicked$value
s = input$myDatatable_cells_selected
if (!is.null(s) && ncol(s) != 0) {
df[df$new_name==s_val,]
} else {
NULL
}
})
})
shinyApp(ui, server)
You can have the value of selected cell with:
s_val = input$myDatatable_cell_clicked$value
After that, you can search this value in your data and print the entire row:
df[df$new_name==s_val,]

Why does rhandsontable dropdown not work with a limted number of items in a objects?

I have adapted scripts from https://github.com/jrowen/rhandsontable/tree/master/inst/examples/rhandsontable_output to learn programming shiny apps.
There you can find the ui.R file too. My server.R file reads as follows:
library(rhandsontable)
#articels.<-read.csv2("/home/rupert/invoiceapp/R/test2/articels.csv",sep=";")
shinyServer(function(input, output, session) {
# this caching step is no longer necessary
# it was left as an example
values = reactiveValues()
data = reactive({
if (!is.null(input$hot)) {
DF = hot_to_r(input$hot)
} else {
if (is.null(values[["DF"]]))
DF = data.frame(Article=letters[1:2],Price=LETTERS[1:2],stringsAsFactors = F)
else
DF = values[["DF"]]
}
values[["DF"]] = DF
DF
})
output$hot <- renderRHandsontable({
DF = data()
if (!is.null(DF))#useTypes =as.logical(input$useType)
rhandsontable(DF ,rowHeaders = NULL)%>%
hot_col(col = "Price", type = "dropdown", source = LETTERS)%>%
hot_col(col = "Article", type = "dropdown", source = letters)
})
})
In case I define a data.frame with only a limited numbers of items of an object, the drop down function is not available anymore (LETTERS[1:2] instead of LETTERS).
The wanted result is a table with only a few rows in which I can choose any item of any R object.

Shiny and DT: how to reset an output that depends on calculations over inputs?

I really had trouble finding a title for this question, hope it helps.
I have a fairly complex app for which I'm having trouble resetting an output after an actionButton ("Confirm" on this example) triggers the re-evaluation of a reactiveValues number that feeds a reactive table.
This causes that the selected table only renders once and no matter how many times the table that feeds it changes, it keeps showing the same result as the first time it was rendered.
It will be easy for you to see what I mean from this example. Believe me, it is the minimax from the one I'm coming from:
library(shiny)
library(DT)
ui <- fluidPage(
DTOutput("table"),
actionButton("checkvalues", "Check")
)
server <- function(input, output, session) {
primedata <- reactiveValues(data = NULL)
primedata$data <- as.numeric(Sys.time()) %% 10000
tabledata <- reactive({
data <- data.frame(rep(primedata$data, 5))
for (i in 1:5) {
data$V1[i] <- as.character(selectInput(paste0("sel", i), "",
choices = c("None selected" = 0,
"Icecream", "Donut"),
selected = 0, width = "120px"))
}
return(data)
})
output$table <- renderDataTable( #Generar tabla
tabledata(), filter = 'top', escape = FALSE, selection = 'none', server = FALSE,
callback = JS("table.rows().every(function(i, tab, row) {
var $this = $(this.node());
$this.attr('id', this.data()[0]);
$this.addClass('shiny-input-container');
});
Shiny.unbindAll(table.table().node());
Shiny.bindAll(table.table().node());")
)
# helper function for reading inputs in DT
shinyValue = function(id, len) {
unlist(lapply(seq_len(len), function(i) {
value = input[[paste0(id, i)]]
if (is.null(value)) NA else value
}))
}
observeEvent(input$checkvalues, {
datos <- tabledata()
selected <- cbind(datos, data.frame(N = shinyValue("sel", nrow(datos))))
selected <- selected %>% group_by(N) %>% summarise("see" = n())
showModal(modalDialog(
title = HTML('<h3 style="text-align:center;">Problem: this table will keep showing the same results as the first one presented</h3>'),
renderDT(datatable(selected, options = list(dom = 't', ordering = F))),
footer = actionButton("Confirm", "Confirm")))
})
observeEvent(input$Confirm, {
primedata$data <- as.numeric(Sys.time()) %% 10000
removeModal()
})
}
shinyApp(ui, server)
When you change primedata$data (by clicking on the Confirm button) this re-renders the table, and you have to unbind before:
ui <- fluidPage(
tags$head(tags$script(
HTML(
"Shiny.addCustomMessageHandler('unbindDT', function(id) {
var $table = $('#'+id).find('table');
if($table.length > 0){
Shiny.unbindAll($table.DataTable().table().node());
}
})")
)),
DTOutput("table"),
actionButton("checkvalues", "Check")
)
observeEvent(input$Confirm, {
session$sendCustomMessage("unbindDT", "table")
primedata$data <- as.numeric(Sys.time()) %% 10000
removeModal()
})

use rhandsontable package to edit multiple data frame on shiny

I am new to the shiny, I would like to edit different multiple data frames by radio button or selectinput by using rhandsontable package. However, my script can not show other data frame but only the first one, I don't know what is the problem.
ui.R:
library(rhandsontable)
fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("select2", label = h3("Choose to edit"),
choices = list("003.csv", "004.csv", "005.csv",
"006.csv", "007.csv"),
selected = "003.csv"),
actionButton("saveBtn", "Save changes")
),
mainPanel(
rHandsontableOutput("hot")
)))
server.R
values <- reactiveValues()
setHot <- function(x) values[["hot"]] <<- x
function(input, output, session) {
fname <- reactive({
x <- input$select2
return(x)
})
observe({
input$saveBtn # update csv file each time the button is pressed
if (!is.null(values[["hot"]])) {
write.csv(x = values[["hot"]], file = fname(), row.names = FALSE)
}
})
output$hot <- renderRHandsontable({
if (!is.null(input$hot)) { # if there is an rhot user input...
DF <- hot_to_r(input$hot) # convert rhandsontable data to R object
and store in data frame
setHot(DF) # set the rhandsontable values
} else {
DF <- read.csv(fname(), stringsAsFactors = FALSE) # else pull table from the csv (default)
setHot(DF) # set the rhandsontable values
}
rhandsontable(DF) %>% # actual rhandsontable object
hot_table(highlightCol = TRUE, highlightRow = TRUE, readOnly = TRUE) %>%
hot_col("Status", readOnly = FALSE)
})}
I can edit and save the dataframe that it shows the first one 003.csv, however when i use the drop down list to 004.csv, it didn't show the dataframe. please advise.
This will write (and possibly overwrite ⚠ any existing file with) dummy data:
for (i in c("003.csv", "004.csv", "005.csv", "006.csv", "007.csv")) {
write.csv(cbind(V1 = rep(i, 3), Status = "foo"), i, row.names = FALSE)
}
I overhauled server a bit:
library(shiny)
library(rhandsontable)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(
"select2", label = h3("Choose to edit"), selected = "003.csv",
choices = list("003.csv", "004.csv", "005.csv", "006.csv", "007.csv")
),
actionButton("saveBtn", "Save changes")
),
mainPanel(
rHandsontableOutput("hot")
)
)
)
server <- function(input, output, session) {
DF <- reactiveVal()
observe({
DF(read.csv(input$select2, stringsAsFactors = FALSE))
})
observe({
if (!is.null(input$hot)) DF(hot_to_r(input$hot))
})
observeEvent(input$saveBtn, {
if (!is.null(DF())) write.csv(DF(), input$select2, row.names = FALSE)
})
output$hot <- renderRHandsontable({
rhandsontable(DF()) %>%
hot_table(highlightCol = TRUE, highlightRow = TRUE, readOnly = TRUE) %>%
hot_col("Status", readOnly = FALSE)
})
}
shinyApp(ui, server)

Get selected rows of Rhandsontable

I am using rhandsontable in a Shiny App and I would like to know how to use the getSelected() method of Handsontable in this case, as I intend to apply changes on the data.frame.
thank you!
You can obtain the selected row, column, range, and cell values, as well as the edited cells using selectCallback=TRUE. You can edit a cell by double-clicking on it, and accept the changes by pressing "return" or "enter".
Minimal example:
library(shiny)
library(rhandsontable)
ui=fluidPage(
rHandsontableOutput('table'),
verbatimTextOutput('selected')
)
server=function(input,output,session)({
df=data.frame(N=c(1:10),L=LETTERS[1:10],M=LETTERS[11:20])
output$table=renderRHandsontable(
rhandsontable(df,selectCallback = TRUE,readOnly = FALSE)
)
output$selected=renderPrint({
cat('Selected Row:',input$table_select$select$r)
cat('\nSelected Column:',input$table_select$select$c)
cat('\nSelected Cell Value:',
input$table_select$data[[
input$table_select$select$r]][[input$table_select$select$c]])
cat('\nSelected Range: R',input$table_select$select$r,
'C',input$table_select$select$c,':R',input$table_select$select$r2,
'C',input$table_select$select$c2,sep="")
cat('\nChanged Cell Row Column:',input$table$changes$changes[[1]][[1]],
input$table$changes$changes[[1]][[2]])
cat('\nChanged Cell Old Value:',input$table$changes$changes[[1]][[3]])
cat('\nChanged Cell New Value:',input$table$changes$changes[[1]][[4]])
})
}) # end server
shinyApp(ui = ui, server = server)
While rhandsontable is a real good implementation of handsontable (credit goes to #jrowen), currently it does not include getSelected().
The event of a user altering any cell (including selecting / deselecting a checkbox) is tracked by shiny. This gives the opportunity to use checkboxes to let the user to select (or de-select) one or more rows.
Unfortunately the logic to understand what has been selected needs to be developed on the server side by your code.
The snippet of code below may give you some idea on how to manage it.
options(warn=-1)
library(rhandsontable)
library(shiny)
options(warn=-1)
quantity <- id <- 1:20
label <- paste0("lab","-",quantity)
pick <- FALSE
iris_ <- data.frame(id=id,pick=pick, quantity=quantity,label=label,iris[1:20,] ,stringsAsFactors = FALSE)
mtcars_ <- data.frame(id=id,pick=pick, quantity=quantity,label=label,mtcars[1:20,] ,stringsAsFactors = FALSE)
iris_$Species <- NULL # i.e. no factors
#---------------------------
ui <- fluidPage(
fluidRow(
column(6,rHandsontableOutput('demTb')),
column(3,uiOutput("demSli")),
column(3, radioButtons("inButtn", label=NULL, choices= c("iris","mtcars"), selected = "iris", inline = TRUE))
)
)
server <- function(session, input, output) {
selData <- ""
output$demSli <- renderUI({
if(is.null(input$demTb) ) return()
isolate({
df_ <- hot_to_r(input$demTb)
index <- which(df_$pick==T)
if(length(index)==0) return()
labs <- iris_$label[index]
pages <- "test"
iter <- length(labs)
buttn <- 1
valLabs <- sapply(1:iter, function(i) {
if(is.null(input[[paste0(pages,"d",labs[i],buttn)]] )) {
0
} else { as.numeric(input[[paste0(pages,"d",labs[i],buttn)]]) }
})
#
toRender <- lapply(1:iter, function(i) {
sliderInput(inputId = paste0(pages,"d",labs[i],buttn),
label = h6(paste0(labs[i],"")),
min = -100,
max = 100,
step = 1,
value = valLabs[i],
post="%",
ticks = FALSE, animate = FALSE)
})
})
return(toRender)
})
#--------------------
rds <- reactive({
# if( is.null(input$demTb) ) {
if( input$inButtn == "iris") {
if(selData == "" | selData == "mtcars") {
selData <<- "iris"
return(iris_) # first time for iris
}
} else {
if(selData == "iris" ) {
selData <<- "mtcars"
return(mtcars_) # first time for mtcars
}
}
df_ <- hot_to_r(input$demTb)
isolate({
index <- which(df_$pick==T)
if(length(index)==0) return(df_)
labs <- iris_$label[index]
pages <- "test"
iter <- length(labs)
buttn <- 1
}) # end isolate
valLabs <- sapply(1:iter, function(i) {
if(is.null(input[[paste0(pages,"d",labs[i],buttn)]] )) {
0
} else {
as.numeric(input[[paste0(pages,"d",labs[i],buttn)]])/100
}
})
dft_ <- data.frame(label=labs, multi=valLabs, stringsAsFactors = FALSE)
dft_ <- merge(iris_,dft_,by="label", all.x=T)
dft_$quantity <- sapply(1:length(dft_$quantity), function(z) {
if( is.na( dft_$multi[z]) ) {
dft_$quantity[z]
} else { iris_$quantity[z]*(1 + dft_$multi[z]) }
})
dft_[with(dft_,order(as.numeric(id))),]
df_[with(df_,order(as.numeric(id))),]
df_$quantity <- df_$quantity
return(df_)
})
output$demTb <- renderRHandsontable({
if(is.null(rds() )) return()
df_ <- rds()
df_ <- df_[with(df_,order(as.numeric(id))),]
rhandsontable(df_, readOnly = FALSE, rowHeaders= NULL, useTypes= TRUE) %>%
hot_table(highlightCol = TRUE, highlightRow = TRUE)
})
}
shinyApp(ui, server)

Resources