DTedit
Abstract
Editable DataTables (DT) package for shiny apps. Originally created by Jason Bryer. Data columns to view or edit can be selected. Different datatypes can be edited. Valid input choices and interaction with persistent storage can be defined through callbacks. In the case of limited selection choices for data columns with ‘dropdown’ inputs, the choices can be defined and dynamically changed. Available in general-purpose and module versions.
Package version : 2.3.1
Github source : David Fong’s Github page
Introduction
The DTedit package allows the viewing and editing of dataframes, via the DT (DataTables for shiny) package.
An entry (row) of the viewed datatable is selected, and can then be edited, copied or deleted. New entries (rows) can be added.
Numerous data types (alphanumeric characters, passwords, numbers, dates, ‘categorical’/factors and raw ‘binary’) can be edited. The data columns which are viewed or edited can be chosen. In the case where choices are restricted (‘categorical’/factor, or if required, alphanumeric), the available choices can be dynamically chosen.
Using callbacks it is possible to further verify whether an entry/row has been modified in an acceptable way. callbacks can also be used to enable persistent storage of modified dataframes into databases.
dtedit
(‘standard’ version) and dteditmod
(‘module’ version) both return reactive versions of the editable dataframe. They also both accept reactive dataframes, and can change their copy of the dataframe in response to changes in the original reactive dataframe.
This vignette will describe the basic usage and features of DTedit
.
Full option/argument descriptions can be found in the appendix.
Usage
Getting started
‘Standard’ version dtedit
For the ‘standard’ (non-module) version, the dtedit
object is defined within the server
function. The name
defined in the dtedit
function (in this case Grocery_List) is referenced by a uiOutput
object in the ui
(user interface) definition, as seen in the very simple example below.
# minimal DTedit example 'dtedit'
# you can try this example in interactive mode
# with 'example("dtedit")'
library(shiny)
library(DT)
library(DTedit)
<- function(input, output) {
server
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
)
)
}
<- fluidPage(
ui h3('Grocery List'),
uiOutput('Grocery_List')
)
shinyApp(ui = ui, server = server)
#> Warning in get_current_theme(): This functionality requires shiny v1.6 or higher
input
and output
in dtedit
is the same as is received by the server
function. thedata
is a dataframe.
Modular version dteditmod
The module version dteditmod
is very similar to dtedit
, and accepts many of the same arguments.
dteditmod
is referenced by the callModule
function, which in turn is defined in shiny’s server
function. Instead of using name
, dteditmod
uses id
(in this case Grocery_List). The same id
is referenced by dteditmodUI
object in the ui
(user interface) definition, surrounded by a shiny::NS
function call, as seen in the example below.
# minimal DTedit example 'dteditmod'
library(shiny)
library(DTedit)
<- function(id) {
myModuleUI <- shiny::NS(id)
ns ::tagList(
shinydteditmodUI(ns('Grocery_List'))
)
}
<- function(input, output, session) {
myModule <- shiny::callModule(
Grocery_List_Results
dteditmod,id = 'Grocery_List',
thedata = data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
)
)
}
<- fluidPage(
ui h3('Grocery List'),
myModuleUI('myModule1')
)
<- function(input, output, session) {
server ::callModule(myModule, 'myModule1')
shiny
}
shinyApp(ui = ui, server = server)
In this vignette, the ‘standard’ dtedit
is mostly used. Some examples referenced, but not fully described, in the DTedit
package use the modular dteditmod
.
Receiving changes from dtedit
(or dteditmod
)
dtedit
returns a list. The list includes $thedata
, which is a reactive and will change when dtedit
’s copy of the dataframe changes. The list also includes $rows_selected
, which is a reactive and will change when a different row is selected.
In the example below, the results are stored in a variable name Grocery_List_Results. Every time the internal copy of thedata
changes, so does Grocery_List_Results$thedata.
An observeEvent
waits for changes in Grocery_List_Results$thedata.
The observeEvent
then messages the contents of Grocery_List_Results$thedata to the console.
Similarly, an observeEvent
waits for changes in Grocery_List_Results$rows_selected.
<- function(input, output) {
server
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
)
)
observeEvent(Grocery_List_Results$thedata, {
message(Grocery_List_Results$thedata)
})
observeEvent(Grocery_List_Results$rows_selected, ignoreNULL = FALSE, {
# 'no' (NULL) row will be 'selected' after each edit of the data
message(paste("Selected row:", Grocery_List_Results$rows_selected))
})
}
<- fluidPage(
ui h3('Grocery List'),
uiOutput('Grocery_List')
)
shinyApp(ui = ui, server = server)
Callbacks
A more sophisticated approach to handling changing of the data is by using callbacks.
In the example below, callback.update
is defined, and checks whether the purchase Quantity
is less than zero. If Quantity
is less than zero, an error is thrown, and it is not possible to store the updated data.
Note that callback.insert
has been defined differently, and so it is still possible to create a new entry/row which has a negative purchase amount! In grocery.insert.callback
, new entries must have a non-empty Buy entry, and does not allow more than ten quantity. Of course, grocery.update.callback
and grocery.insert.callback
could be re-defined to be consistent with each other.
Note that grocery.insert.callback
combines error messages, and makes use of HTML line breaks.
<- function(input, output) {
server
<- function(data, olddata, row) {
grocery.update.callback # 'data' contains the dataframe *after* the row has been updated
# 'row' is the row number where data has been updated
# 'olddata' is the previous version of the data
if (data[row, "Quantity"] < 0) {
stop("Can't buy less than zero (0)!")
}
return(data)
}
<- function(data, row) {
grocery.insert.callback # 'data' contains the dataframe *after* the row has been inserted
<- list()
msg if (data[row, "Buy"] == "") {
1]] <- "Item name can't be empty!"
msg[[
}if (data[row, "Quantity"] > 10) {
2]] <- "Can't buy more than ten (10)!"
msg[[
}<- Filter(Negate(is.null), msg)
msg if (length(msg)) stop(paste(msg, collapse = "<br>"))
return(data)
}
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
),callback.update = grocery.update.callback,
callback.insert = grocery.insert.callback
)
}
<- fluidPage(
ui h3('Grocery List'),
uiOutput('Grocery_List')
)
shinyApp(ui = ui, server = server)
More sophisticated callbacks can take the opportunity to store changed data in a persistent database. This is demonstrated in dtedit_demo()
. The source code for the demonstration can be seen with
dtedit_demo(options = list(display.mode = "showcase"))
in the app.R
tab.
The modular dteditmod
version of the callbacks demonstration, with multiple callbacks for multiple datatables, can be seen with
dtedit_mod_demo(options = list(display.mode = "showcase"))
in the app_mod.R
tab.
Show a raised exception message inside callback function with tryCatch
Callbacks will often try to store data edited using the modal dialog using ‘SQL’ queries. Sometimes the database query will return an error message (‘raise an exception’).
These exceptions can be caught with tryCatch
, in order to display a more meaningful and less cryptic error message.
In this example, there is a misspelt column in the books.insert.callback
callback, which will result in an exception being raised by the SQL query.
library(shiny)
library(RSQLite)
library(DTedit)
<- dbConnect(SQLite(), "books.sqlite")
conn <- data.frame(
books Author = c("Mary", "John", "Sue"),
Publisher = c("University House", "Random House", "Beyond Books"),
stringsAsFactors = FALSE
)dbWriteTable(conn, "books", books, overwite = TRUE)
dbDisconnect(conn)
<- dbConnect(RSQLite::SQLite(), "books.sqlite")
conn
<- function() {
getBooks <- dbSendQuery(conn, "SELECT * FROM books")
res <- dbFetch(res)
books dbClearResult(res)
$Authors <- books$Authors
books$Publisher <- books$Publisher
booksreturn(books)
}
##### Callback functions.
<- function(data, row) {
books.insert.callback <- paste0(
query "INSERT INTO books (Author, Publishxer) VALUES (",
# !!! incorrectly spelt column 'Publishxer' !!!
"'", paste0(data[row,]$Author[[1]], collapse = ';'), "', ",
"'", as.character(data[row,]$Publisher), "' ",
")")
print(query) # For debugging
tryCatch(
{<- dbSendQuery(conn, query)
res dbClearResult(res)
},error = function(cond) {
stop(paste("Database error: ", cond))
}
)return(getBooks())
}
##### Create the Shiny server
<- function(input, output, session) {
server <- getBooks()
books <- dtedit(
booksdt
input, output,name = 'books',
thedata = books,
edit.cols = c('Author', 'Publisher'),
edit.label.cols = c(
'Author', 'Publisher'
),show.delete = FALSE, show.copy = FALSE, show.update = FALSE,
callback.insert = books.insert.callback,
)
<- session$onSessionEnded(function() {
cancel.onsessionEnded ::dbDisconnect(conn)
DBI
})
}
##### Create the shiny UI
<- fluidPage(
ui h3('Books'),
uiOutput('books'),
)
shinyApp(ui = ui, server = server)
inputEvent
- observing input events from the New/Edit/Copy modal dialogs
observeEvent
s, to watch for changes in the input dialogs, are created with the help of the inputEvent
parameter. This is particularly useful for creating feedback to the user ‘on the fly’. It was designed to use together with shinyfeedback
, but does not depend on shinyfeedback
.
In the example below, which uses shinyfeedback
, inputEvent
is a named list, including functions with the names of editable columns. The functions takes an inputId
argument of the input dialog which is related to the editable column, and the value of the input dialog. The function can then use the value of the input dialog, create shinyfeedback
messages, or even change the contents of the input dialog.
(Note the comments in the example regarding peculiarities when using modules)
library(shiny)
library(DTedit)
<- function(input, output) {
server
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
),inputEvent = list(
Quantity = function(x, value) {
# x will be the inputID of the 'Quantity' widget
# value will be the contents of the input widget
#
# if *not* called as a module, then value == input[[x]]
# however, if 'dteditmod' is called, then value != input[[x]] because
# 'x' does *not* include the ID 'prefix' of the module. (the module's
# ID prefix could be added 'manually' by the function, potentially).
# Strangely, the inputId parameter of shinyFeedback can use 'x'
# without any modification, even if 'dteditmod' is called.
# if the input box is empty, value will be NA
if (!is.na(value) && value < 0) {
::showFeedbackWarning(
shinyFeedbackinputId = x,
text = "Less than zero"
)else {
} ::hideFeedback(x)
shinyFeedback
}if (!is.na(value) && value > 10 && value <= 100) {
::showToast("success", "That's a lot!")
shinyFeedback
}if (!is.na(value) && value > 100) {
::showToast("error", "Too many, try again!")
shinyFeedback::updateNumericInput(
shinysession = shiny::getDefaultReactiveDomain(),
inputId = x,
value = NA
)
}
},Buy = function(x, value) {
if (value == "") {
::showFeedbackDanger(
shinyFeedbackinputId = x,
text = "Name must not be empty!"
# note that this is just a warning, really
# since this feedback itself does not
# enforce the warning.
)else {
} ::hideFeedback(x)
shinyFeedback
}
}
)
)
}
<- fluidPage(
ui ::useShinyFeedback(),
shinyFeedbackh3('Grocery List'),
uiOutput('Grocery_List')
)
shinyApp(ui = ui, server = server)
Reactive thedata
dataframe
thedata
can be a reactive reactiveVal
, in which case dtedit
’s copy of the thedata
will change in response to changes in the original dataframe. In the example below, the input buttons more and less trigger changes in mydata, which will be detected by dtedit
.
In the call to dtedit
, mydata is referred to in the thedata
definition as mydata, not mydata().
Changes in dtedit
’s copy of the data as the result of editing within dtedit
do not automatically change the original dataframe mydata. The example below has set up an observeEvent
to
- detect changes in Grocery_List_Results$thedata
- which then changes mydata
Note that Grocery_List_Results$thedata does not have trailing parentheses ‘()’.
<- function(input, output) {
server
<- reactiveVal({
mydata data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
)
})
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = mydata
)
observeEvent(input$more, {
# if the 'Buy More!' button is pressed
<- data.frame(
newdata Buy = mydata()$Buy,
Quantity = mydata()$Quantity * 2,
# doubles the quantity
stringsAsFactors = FALSE
)mydata(newdata)
})
observeEvent(input$less, {
# if the 'Too Much!' button is pressed
<- data.frame(
newdata Buy = mydata()$Buy,
Quantity = mydata()$Quantity * 0.5,
# halves the quantity
stringsAsFactors = FALSE
)mydata(newdata)
})
observeEvent(Grocery_List_Results$thedata, {
# the data has been added
# copy the changes to our own copy
mydata(Grocery_List_Results$thedata)
})
}
<- fluidPage(
ui h3('Grocery List'),
uiOutput('Grocery_List'),
actionButton(inputId = "more", label = "Buy More!"),
actionButton(inputId = "less", label = "Too Much!")
)
shinyApp(ui = ui, server = server)
Another example of reactive dataframes can be seen, with source code, with
dtedit_reactive_demo(options = list(display.mode = "showcase"))
the code is in the app.R
tab. A modular version, using dteditmod
, can be seen with example(dteditmodUI)
.
View and Edit columns
Sometimes not all columns of a dataframe should be viewed, or edited. The columns to edit or view can be defined.
In the example below, edit.cols
is defined such that only Quantity and Comment columns can be edited. The Quantity column is not seen in the datatable view, because it is not included in the view.cols
definition.
In addition, delete, add and copy buttons have been turned off with show.delete
, show.insert
and show.copy
options all set to FALSE
.
<- function(input, output) {
server
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Quantity = c(7, 2, 5),
Comment = c('Irish', 'Not too sweet', 'Jonathan'),
stringsAsFactors = FALSE
),edit.cols = c('Quantity', 'Comment'),
view.cols = c('Buy', 'Comment'),
show.delete = FALSE,
show.insert = FALSE,
show.copy = FALSE
)
}
<- fluidPage(
ui h3('Grocery List'),
uiOutput('Grocery_List')
)
shinyApp(ui = ui, server = server)
Formatting columns : datatable.call
and datatable.rownames
DTedit
uses DT
by calling DT::renderDataTable
. DT
provides helper functions format*()
to format table columns as currency, percentages or round numbers. To use the helper functions, the data needs to be pre-processed by DT::datatable
and DT::format*()
before being passed to DT::renderDataTable
. These helper functions can be accessed by defining datatable.call
as a pre-processing function.
The formatting demonstrated below is not actually appropriate to the mtcars
data, but is used for demonstration purposes. The formatting does not work unless datatable.rownames
is set to TRUE
.
library(shiny)
library(DTedit)
library(magrittr) # provides the pipe '%>%' operator
<- function(input, output, session) {
server dtedit(
input, output,name = 'mtcarstable',
thedata = mtcars,
datatable.rownames = TRUE, # needed for the format*() functions to work
datatable.call = function(...) {
::datatable(...) %>%
DT::formatSignif('qsec', 2) %>%
DT::formatCurrency('mpg') %>%
DT::formatStyle(
DT'cyl',
color = 'red', backgroundColor = 'orange', fontWeight = 'bold'
)# note, none of this is proper formatting for the mtcars data!
# but serves to demonstrate the formatting
}
)
}
<- fluidPage(
ui h3('mtcars'),
uiOutput('mtcarstable')
)
shinyApp(ui = ui, server = server)
By default, datatable.call
is defined as :
function(...) {DT::datatable(...)}
DTedit
will pass several arguments to datatable.call
.
data
a dataframe. May have been processed to addactionButtons
options
-datatable.options
rownames
-datatable.rownames
escape
- escape all columns except those withactionButtons
.selection
-single
input.types
The data class of the column determines the default input type of the column. The mappings are :
- list \(\rightarrow\) “selectInputMultiple”
- character \(\rightarrow\) “textInput”
- Date \(\rightarrow\) “dateInput”
- POSIXct \(\rightarrow\) “datetimeInput”
- factor \(\rightarrow\) “selectInput”
- integer \(\rightarrow\) “numericInput”
- numeric \(\rightarrow\) “numericInput”
- logical \(\rightarrow\) “checkboxInput”
- blob \(\rightarrow\) “fileInput”
input.types
can be explicitly defined.
Valid input.types
are :
- dateInput
- datetimeInput - note this requires
shinyWidgets
(>= 0.5.2) package anduseairDatepicker
option. - selectInput
- selectInputMultiple
- selectInputReactive
- selectInputMultipleReactive
- selectizeInput
- selectizeInputMultiple
- selectizeInputReactive
- selectizeInputMultipleReactive
- numericInput
- textInput
- textAreaInput
- checkboxInput
- passwordInput
- fileInput
For an example of explicit definition, see selectInput
.
selectInput
and selectInputReactive
Drop down choices can be defined statically by defining input.types
as selectInput
. selectInput
will look for possible input choices in the following ways :
- if
input.choices
is defined (as in the example below) - the column is a ‘factor/categorical’ variable, with defined
factor
s - the pre-existing values already present in the column.
Drop down choices can be changed dynamically by defining input.types
as selectInputReactive
. The input.choices
for a selectInputReactive
is a list name (in this case buy.types.list
) in input.choices.reactive
. buy.types.list
in input.choices.reactive
defines a reactive variable, which in the example below is buy.Types
.
In the example below, if the radioButton choices
is changed to More
(value = 2), then the reactive variable buy.Types is changed to the longer vector of grocery names.
<- function(input, output) {
server
<- c('Tea', 'Biscuits', 'Apples', 'Cheese')
less_choices <- c(less_choices, 'Coffee', 'Pears', 'Fish')
more_choices
<- reactiveVal(less_choices)
buy.Types
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = data.frame(
Buy = c('Tea', 'Biscuits', 'Apples'),
Type = c('Plant', 'Processed', 'Fruit'),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
),input.types = list(
Buy = 'selectInputReactive',
Type = 'selectInput'
),input.choices = list(
Buy = 'buy.Types.list',
Type = c('Plant', 'Processed', 'Fruit', 'Animal')
),input.choices.reactive =
list(buy.Types.list = buy.Types)
)
observeEvent(input$choice, {
if (input$choice == 1) {
buy.Types(less_choices)
else {
} buy.Types(more_choices)
}
})
}
<- fluidPage(
ui h3('Grocery List'),
uiOutput('Grocery_List'),
radioButtons(
'choice',
label = 'Buy choices',
choices = list('Less' = 1, 'More' = 2),
selected = 1
)
)
shinyApp(ui = ui, server = server)
selectInputReactive
is demonstrated with
dtedit_selectInputReactive_demo(options = list(display.mode = "showcase"))
with the source code in the app.R
tab. In this demonstration, names.Types (user types) and names.Likes (potential user Likes) are reactive variables which are used to determine the choices to selectInputReactive
and selectInputMultipleReactive
in the names DataTable. names.Types and names.Likes are themselves altered by changes in two other DTedit
datatables.
The same demonstration is shown as part of the demonstration using modular dteditmod
dteditmod_demo(options = list(display.mode = "showcase"))
with source code in the app_mod.R
tab. The interacting datatables are in the Emails
tab.
selectizeInput
(and other selectize...
variants)
Each of selectInput
, selectInputMultiple
, selectInputReactive
and selectInputMultipleReactive
have selectize...
variants. If more than one selectize...
input type is being used, then the selectize
options can be individually defined in selectize.options
as a named list, as in the example below.
This example uses selectize.options
to limit the number of multiple selections for FromState
and ToState
, as well as create a placeholder for Store
.
library(shiny)
library(DTedit)
<- function(input, output) {
server
<- list(
less_states Eastern = c(`New York` = 'NY', `New Jersey` = 'NJ'),
Western = c(`California` = 'CA', `Washington` = 'WA')
)<- list(
more_states Eastern = c(`New York` = 'NY', `New Jersey` = 'NJ'),
Midwest = c(`Illinois` = 'IL', `Indiana` = 'IN', `Minnestota` = 'MN'),
Western = c(`California` = 'CA', `Washington` = 'WA')
)<- c('clothes', 'food', 'toys')
less_product <- c('clothes', 'food', 'toyes', 'tea', 'coffee')
more_product
<- reactiveVal(less_states)
from.states <- reactiveVal(less_product)
product.types
<- dtedit(
Grocery_List_Results
input, output,name = 'Grocery_List',
thedata = data.frame(
Store = c('stor1', 'stor1', 'stor2'),
Product = c('food', 'clothes', 'clothes'),
FromState = I(list(list('CA', 'NJ'), list('WA'), list('NY'))),
ToState = I(list(list('CA'), list('WA'), list('NY'))),
Quantity = c(7, 2, 5),
stringsAsFactors = FALSE
),edit.label.cols = c('Store', 'Product', 'From State', 'To State', 'Quantity'),
input.types = list(
Store = 'selectizeInput',
Product = 'selectizeInputReactive',
FromState = 'selectizeInputMultipleReactive',
ToState = 'selectizeInputMultiple'
),input.choices = list(
Store = c("stor1","stor2"),
Product = 'product.list',
FromState = 'from.states.list',
ToState = less_states
),input.choices.reactive =
list(from.states.list = from.states,
product.list = product.types),
selectize.options = list(
Store = list(
placeholder = "Please select an option below",
onInitialize = I('function() { this.setValue(""); }')
),FromState = list(create = TRUE, maxItems = 2),
ToState = list(create = TRUE, maxItems = 3)
)
)
observeEvent(input$choice_states, {
if (input$choice_states == 1) {
from.states(less_states)
else {
} from.states(more_states)
}
})observeEvent(input$choice_product, {
if (input$choice_product == 1) {
product.types(less_product)
else {
} product.types(more_product)
}
})
}
<- fluidPage(
ui h3('Grocery List'),
uiOutput('Grocery_List'),
radioButtons(
inputId = 'choice_states',
label = "'From State' choices",
choices = list('Less' = 1, 'More' = 2),
selected = 1
),radioButtons(
inputId = 'choice_product',
label = "Product choices",
choices = list('Less' = 1, 'More' = 2),
selected = 1
)
)
shinyApp(ui = ui, server = server)
Appendix - Help files with full option descriptions
dtedit
and dteditmod
help
dtedit | R Documentation |
Create a DataTable with Add, Edit and Delete buttons.
Description
dtedit - editable DataTable
dteditmod - editable DataTable, adapted for use in modules
Usage
dtedit(input, output, name, thedata, ...) dteditmod( input, output, session, thedata, view.cols = names(shiny::isolate(if (shiny::is.reactive(thedata)) { thedata() } else { thedata })), edit.cols = names(shiny::isolate(if (shiny::is.reactive(thedata)) { thedata() } else { thedata })), edit.label.cols = edit.cols, delete.info.cols = view.cols, delete.info.label.cols = delete.info.cols, input.types, input.choices = NULL, input.choices.reactive = NULL, selectize.options = NULL, inputEvent = NULL, action.buttons = NULL, selectize = TRUE, modal.size = "m", text.width = "100%", textarea.width = "570px", textarea.height = "200px", date.width = "100px", datetime.width = "200px", numeric.width = "100px", select.width = "100%", checkbox.width = "100%", defaultPageLength = 10, max.fileInputLength = 1e+08, title.delete = "Delete", title.edit = "Edit", title.add = "New", label.delete = "Delete", label.edit = "Edit", label.add = "New", label.copy = "Copy", label.save = "Save", label.cancel = "Cancel", icon.delete = NULL, icon.edit = NULL, icon.add = NULL, icon.copy = NULL, text.delete.modal = "Are you sure you want to delete this record?", show.delete = TRUE, show.update = TRUE, show.insert = TRUE, show.copy = TRUE, callback.delete = function(data, row) { }, callback.update = function(data, olddata, row) { }, callback.insert = function(data, row) { }, callback.actionButton = function(data, row, buttonID) { }, click.time.threshold = 0.5, useairDatepicker = FALSE, datatable.options = list(pageLength = defaultPageLength), datatable.rownames = FALSE, datatable.call = function(...) { DT::datatable(...) }, ... )
Arguments
input
|
Shiny input object passed from the server. |
output
|
Shiny output object passed from the server. |
name
|
( |
thedata
|
a data frame to view and edit. can be a reactive |
…
|
arguments not recognized by DTedit are passed to |
session
|
Shiny session object (an environment) passed from the server. Alternatively, the ‘name’ (character) of the outputted editable datatable. |
view.cols
|
character vector with the column names to show in the DataTable. This can be a subset of the full |
edit.cols
|
character vector with the column names the user can edit/add. This can be a subset of the full |
edit.label.cols
|
character vector with the labels to use on the edit and add dialogs. The length and order of |
delete.info.cols
|
character vector with the column names specifying which values are presented on the delete dialog. This can be a subset of the full |
delete.info.label.cols
|
character vector with the labels to use on the delete dialog. The length and order of |
input.types
|
a character vector where the name corresponds to a column in
One case where this parameter is desirable is when a text area is required instead of a simple text input. |
input.choices
|
a list of character vectors. The names of each element in the list must correspond to a column name in the data. The value, a character vector, are the options presented to the user for data entry, in the case of input type
In the case of input type
In the case of input type |
input.choices.reactive
|
a named list of reactives, referenced in |
selectize.options
|
options for |
inputEvent
|
a named list of functions. The names of each element in the list must correspond to an editable column name in the data. The function is called when the associated input widget event is observed during editing/adding a data row. Can be used, for example, with |
action.buttons
|
a named list of action button columns. Each column description is a list of
|
selectize
|
Whether to use |
modal.size
|
the size of the modal dialog. See |
text.width
|
width of text inputs. |
textarea.width
|
the width of text area inputs. |
textarea.height
|
the height of text area inputs. |
date.width
|
the width of data inputs |
datetime.width
|
the width of datetime inputs |
numeric.width
|
the width of numeric inputs. |
select.width
|
the width of drop down inputs. |
checkbox.width
|
the width of checkbox inputs. |
defaultPageLength
|
number of rows to show in the data table by default. |
max.fileInputLength
|
the maximum length (in bytes) of |
title.delete
|
the title of the dialog box for deleting a row. |
title.edit
|
the title of the dialog box for editing a row. |
title.add
|
the title of the dialog box for inserting a new row. |
label.delete
|
the label of the delete button. |
label.edit
|
the label of the edit button. |
label.add
|
the label of the add button. |
label.copy
|
the label of the copy button. |
label.save
|
the label of the save button. |
label.cancel
|
the label of the cancel button. |
icon.delete
|
the icon for the delete button, e.g. |
icon.edit
|
the icon for the edit button, e.g. |
icon.add
|
the icon for the add button, e.g. |
icon.copy
|
the icon for the copy button, e.g. |
text.delete.modal
|
the text shown in the delete modal dialog. |
show.delete
|
whether to show/enable the delete button. |
show.update
|
whether to show/enable the update button. |
show.insert
|
whether to show/enable the insert button. |
show.copy
|
whether to show/enable the copy button. |
callback.delete
|
a function called when the user deletes a row. This function should return an updated data.frame. |
callback.update
|
a function called when the user updates a row. This function should return an updated data.frame. |
callback.insert
|
a function called when the user inserts a new row. This function should return an updated data.frame. |
callback.actionButton
|
a function called when the user clicks an action button. called with arguments |
click.time.threshold
|
This is to prevent duplicate entries usually by double clicking the save or update buttons. If the user clicks the save button again within this amount of time (in seconds), the subsequent click will be ignored (using |
useairDatepicker
|
use |
datatable.options
|
options passed to |
datatable.rownames
|
show rownames as part of the datatable? |
datatable.call
|
pre-processing call when calling
|
Details
dtedit
is used in conjunction with uiOutput
to create editable datatables. dtedit
is used in a shiny application’s server definition, uiOutput
is used in the UI (user interface) definition.
dteditmod
is used in conjunction with callModule
and dteditmodUI
to create editable datatables in a module environment. dteditmod
is called through callModule
in the ‘server’ section of the shiny application. dteditmodUI
is called in the ‘UI’ (user-interface) section of the shiny app.
This object will maintain data state. However, in order for data to persist between Shiny instances, data needs to be saved to an external format (e.g. database or R data file). The callback functions provide a mechanism for this function to interact with a permanent data storage scheme. The callback functions are called when the user adds, updates, or deletes a row from the data table. The callback must accept two parameters: data
and row
. For inserting and updating, the data
object is the current state of data table including any additions or updates. The row
parameter indicates which row from data
was modified (or added). For deletions, however, the data
represents the data table just before deleting the specified row. That is, if callback.delete
returns a data.frame
, that will be the new data table; otherwise this function will remove row row
from data
and that will become the current data table.
While olddata
will contain values as contained in the original data.frame
, the values returned in data
are limited to values that can be returned by the shiny
widgets underlying the input.types
. For example, the textInput
can return an empty string, but cannot return NA
.
The callback functions may throw errors (see e.g. stop
) if there are problems with data. That is, if data validation checks indicate data problems before inserting or updating a row the function may throw an error. Note that the error message will be presented to the user so providing messages meaningful to the user is recommended. Moreover, if an error is thrown, the modal dialog is not dismissed and the user can further edit the data and retry the insertion or update.
Callback functions may return a data.frame
. When a data.frame
is returned that will become the current state of the data table. If anything else is returned then the internal data.frame
will be used.
Value
Returns reactiveValues
-
theData
- the current state ofDTedit
’s copy of the data -
view.cols
-
edit.cols
-
edit.count
- number of edits to data done withinDTedit
(does not include changes toDTedit
’s copy of the data secondary to changes of a reactivethedata
) -
rows_selected
- the row number selected. initially set toNULL
See Also
-
example(“dtedit”)
a simple example. -
dtedit_demo()
demonstration of dtedit. -
dtedit_reactive_demo()
reactive dataframe -
dtedit_selectInputReactive_demo()
reactive selectInput
-
dteditmodUI
: the companion user-interface function fordteditmod
.
-
example(“dteditmodUI”)
a simple module example with reactive dataframe -
dteditmod_demo()
a more complex module example. Database interaction and interactions between the data of multiple datatables. -
dteditmod_fileInput_demo()
a modular example including binary file input and action buttons.
Other Datatable Edit functions: dteditmodUI()
Examples
# minimal DTedit example 'dtedit' # you can try this example in interactive mode # with 'example("dtedit")' library(shiny) library(DTedit) server <- function(input, output) { Grocery_List <- dtedit( input, output, name = 'Grocery_List', thedata = data.frame( Buy = c('Tea', 'Biscuits', 'Apples'), Quantity = c(7, 2, 5), stringsAsFactors = FALSE ) ) } ui <- fluidPage( h3('Grocery List'), uiOutput('Grocery_List') ) if (interactive()) shinyApp(ui = ui, server = server) #### end of 'dtedit' example #### # minimal DTedit example 'dteditmod' # this is a separate application from the 'dtedit' example! # # unfortunately, this application cannot be # tried with 'example("dteditmod")', but you can copy # and paste to execute in 'interactive' console mode, # or copy the lines into an '.R' file and choose # 'Run App' from RStudio. library(shiny) library(DTedit) myModuleUI <- function(id) { ns <- shiny::NS(id) shiny::tagList( dteditmodUI(ns('Grocery_List')) ) } myModule <- function(input, output, session) { Grocery_List_Results <- shiny::callModule( dteditmod, id = 'Grocery_List', thedata = data.frame( Buy = c('Tea', 'Biscuits', 'Apples'), Quantity = c(7, 2, 5), stringsAsFactors = FALSE ) ) } server <- function(input, output, session) { shiny::callModule(myModule, 'myModule1') } ui <- fluidPage( h3('Grocery List'), myModuleUI('myModule1') ) if (interactive() || isTRUE(getOption("shiny.testmode"))) shinyApp(ui = ui, server = server)
dteditmodUI
help
dteditmodUI | R Documentation |
Create a DataTable with Add, Edit and Delete buttons.
Description
dteditmodUI - user-interface function for module use
Usage
dteditmodUI(id)
Arguments
id
|
the namespace of the module |
Details
Use in conjunction with callModule
and dtedit
to create editable datatables. dteditUI
is used in the ‘user interface’ component of the shiny app.
See Also
dteditmod
: the companion server-component function.
-
example(“dteditmodUI”)
a simple example with a reactive dataframe -
dteditmod_demo()
a more complex example. Includes database interaction and interactions between the data of multiple datatables.
Other Datatable Edit functions: dtedit()
Examples
##### Minimal DTedit example using reactive dataframe ##### library(shiny) library(DTedit) ##### module ###################### myModuleUI <- function(id) { ns <- shiny::NS(id) shiny::tagList( dteditmodUI(ns("dataspace")) ) } myModule <- function(input, output, session, data_scramble) { data <- reactiveVal() # # 'data' will be a 'reactive' dataframe data(data.frame(Column1 = c("Apple", "Cherry", "Frozen"), Column2 = c("Pie", "Tart", "Yoghurt"), stringsAsFactors = FALSE)) data_DT_gui <- callModule( dteditmod, "dataspace", thedata = data, edit.cols = c("Column1", "Column2") ) observe({ data( isolate( as.data.frame( data_DT_gui$thedata, stringsasfactors = FALSE ) ) ) print(isolate(data())) print(paste("Edit count:", data_DT_gui$edit.count)) # only reacts to change in $edit.count }) observeEvent(data_scramble(), { print("Scrambling...") temp <- data() if (nrow(temp) > 0) { row <- sample(seq_len(nrow(temp)), 1) # row col <- sample(1:2, 1) # column temp[row, col] <- paste( sample(unlist(strsplit(temp[row, col], "")), nchar(temp[row, col])), sep = '', collapse = '') data(temp) # adjusted dataframe 'automatically' read by DTedit } }) } ##### Create the Shiny server ##### server <- function(input, output) { data_scramble <- shiny::reactive({ input$data_scramble }) shiny::callModule(myModule, "myModule1", data_scramble) } ##### Create the shiny UI ###### ui <- fluidPage( h3("DTedit using reactive dataframe"), wellPanel(p("Try the 'Scramble' button!")), myModuleUI("myModule1"), actionButton("data_scramble", "Scramble an entry") ) if (interactive() || isTRUE(getOption("shiny.testmode"))) shinyApp(ui = ui, server = server)