Reputation: 21322
The small app below generates a DT::datatable with two columns x,y. X starts out being a random number with rnorm. y should be whatever x is plus 1.
The app allows the user to edit column x in a DT::datatable. I have built it so that the user can amend column x, however, column y does not update as expected, it just stays the same.
Shiny code:
library(shiny)
library(tidyverse)
library(shinydashboard)
library(scales)
library(DT)
# define functions
## generate example data
create_sample_df <- function(x) {
data.frame(
x = x %>% unlist
) %>% mutate(y = x + 1)
}
## render DT
render_dt = function(data, editable = 'cell', server = TRUE, ...) {
renderDT(data, selection = 'none', server = server, editable = editable, ...)
}
# UI ----
header <- dashboardHeader(title = 'blah')
sidebar <- dashboardSidebar()
body <- dashboardBody(DT::DTOutput('ex_df'))
ui <- dashboardPage(header, sidebar, body)
# Server ----
server <- function(input, output) {
x <- rnorm(10, 0, 2) %>% as.integer %>% as.list
# the df to be displayed as a DT::datatable.
ex_df <- reactive({create_sample_df(x)})
## set to initially be the on open result of ex_df, before any user input
reactivs <- reactiveValues(ex_df = ex_df)
observeEvent(input$ex_df_cell_edit, {
info = input$ex_df_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
# update budgets, which in turn is used to generate data during create_sample_df()
x[[i]] <<- v
# now update the reactive values object with the newly generated df
reactivs$ex_df <<- reactive({create_sample_df(x)})
})
output$ex_df <- render_dt(data = reactivs$ex_df(),
rownames = FALSE,
list(target = 'cell',
disable = list(columns = c(1))))
}
shinyApp(ui, server)
In the screen I am about to edit the first row in column x from -1 to say 10. After hitting enter, desired result is that for row 1, x value is 10 an y value is 11.
Currently this does not happen, y stays the same no matter what. Also, The first attempt to edit column x does not work, only after the second attempt does the new value persist.
Upvotes: 1
Views: 644
Reputation: 10375
The problem is how you pass the reactive data to your custom render_dt
. I'm not completely sure why, but changes to reactivs$ex_df
are not recognised. The changes you see in the x column are not due to the updated ex_df
, but the changes directly made in the table. Therefore, I changed it back to using renderDT
directly. I've made some additional changes:
ex_df
itself is not reactive. It is stored in a reactiveValues
object, where every entry itself is already reactive.reactiveValues
don't need <<-
cell_edit
is a character vectorlibrary(shiny)
library(tidyverse)
library(shinydashboard)
library(scales)
library(DT)
# define functions
## generate example data
create_sample_df <- function(x) {
data.frame(
x = x %>% unlist
) %>% mutate(y = x + 1)
}
# UI ----
header <- dashboardHeader(title = 'blah')
sidebar <- dashboardSidebar()
body <- dashboardBody(DT::DTOutput('ex_df'))
ui <- dashboardPage(header, sidebar, body)
# Server ----
server <- function(input, output) {
x <- rnorm(10, 0, 2) %>% as.integer %>% as.list
# the df to be displayed as a DT::datatable.
ex_df <- create_sample_df(x)
## set to initially be the on open result of ex_df, before any user input
reactivs <- reactiveValues(ex_df = ex_df)
observeEvent(input$ex_df_cell_edit, {
info = input$ex_df_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
# update budgets, which in turn is used to generate data during create_sample_df()
x[[i]] <<- as.numeric(v)
# now update the reactive values object with the newly generated df
reactivs$ex_df <- create_sample_df(x)
})
output$ex_df <- renderDT({
datatable(reactivs$ex_df,
editable = "cell",
rownames = FALSE,
options = list(target = 'cell',
disable = list(columns = c(1))))
})
}
shinyApp(ui, server)
here a solution without observeEvent
and only a reactive()
for ex_df
. Then you can pass the unevaluated reactive
to your render_dt
function:
library(shiny)
library(tidyverse)
library(shinydashboard)
library(scales)
library(DT)
# define functions
## generate example data
create_sample_df <- function(x) {
data.frame(
x = x %>% unlist
) %>% mutate(y = x + 1)
}
## render DT
render_dt = function(data_in, editable = 'cell', server = TRUE, ...) {
renderDT(data_in(), selection = 'none', server = server, editable = editable, ...)
}
# UI ----
header <- dashboardHeader(title = 'blah')
sidebar <- dashboardSidebar()
body <- dashboardBody(DT::DTOutput('ex_df'))
ui <- dashboardPage(header, sidebar, body)
# Server ----
server <- function(input, output) {
x <- rnorm(10, 0, 2) %>% as.integer %>% as.list
# the df to be displayed as a DT::datatable.
setup_df <- create_sample_df(x)
ex_df <- eventReactive(input$ex_df_cell_edit, {
# on startup
if (is.null(input$ex_df_cell_edit)) {
setup_df
# for edits
} else {
info = input$ex_df_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
# update budgets, which in turn is used to generate data during create_sample_df()
x[[i]] <<- as.numeric(v)
create_sample_df(x)
}
},
ignoreNULL = FALSE)
output$ex_df <- render_dt(data_in = ex_df,
rownames = FALSE,
list(target = 'cell',
disable = list(columns = c(1))))
}
shinyApp(ui, server)
Upvotes: 2