I have an app which has a DT table with row selection enabled. Is it possible to toggle between disabling and enabling the entire table without rerendering it? Using shinyjs::disable and shinyjs::enable will disable and enable the text boxes in the table used for filtering, but not the ability to select rows. I'm also not aware of any DT proxy method that would accomplish what I'm trying to do.
library(shiny)
library(DT)
library(shinyjs)
ui <- fluidPage(
DTOutput("table"),
actionButton(inputId = "disable",
label = "Disable"),
actionButton(inputId = "enable",
label = "Enable"),
useShinyjs()
)
server <- function(input, output, session) {
output$table <- renderDT({
data <- data.frame(COL_1 = c(1, 2, 3, 4),
COL_2 = c("A", "B", "C", "D"),
stringsAsFactors = FALSE)
datatable(data,
escape = FALSE,
filter = list(position = "top"),
rownames = FALSE)
})
observeEvent(input$disable, {
disable(id = "table")
})
observeEvent(input$enable, {
enable(id = "table")
})
}
shinyApp(ui = ui, server = server)
It seems there is no proxy method fitting your needs, accordingly you'll have to re-render your table.
However, you don't need library(shinyjs).
Please check the following:
library(shiny)
library(DT)
ui <- fluidPage(
DTOutput("table"),
actionButton(inputId = "disable",
label = "Disable"),
actionButton(inputId = "enable",
label = "Enable")
)
server <- function(input, output, session) {
dtSettings <- reactiveValues(searchable = TRUE, mode = "multiple")
output$table <- renderDT({
data <- data.frame(COL_1 = c(1, 2, 3, 4),
COL_2 = c("A", "B", "C", "D"),
stringsAsFactors = FALSE)
datatable(data,
escape = FALSE,
filter = list(position = "top"),
rownames = FALSE,
selection = list(mode = dtSettings$mode , selected = NULL, target = 'row'),
options = list(
columnDefs = list(list(targets = seq_len(ncol(data))-1, searchable = dtSettings$searchable)),
pageLength = 5
))
}, server = FALSE)
observeEvent(input$disable, {
dtSettings$searchable <- FALSE
dtSettings$mode <- "none"
})
observeEvent(input$enable, {
dtSettings$searchable <- TRUE
dtSettings$mode <- "multiple"
})
}
shinyApp(ui = ui, server = server)
I figured out that I could accomplish this by using event.stopPropagation() within the tbody element of the datatable.
library(shiny)
library(DT)
library(shinyjs)
ui <- fluidPage(
DTOutput("table"),
actionButton(inputId = "disable",
label = "Disable"),
actionButton(inputId = "enable",
label = "Enable"),
useShinyjs()
)
server <- function(input, output, session) {
output$table <- renderDT({
data <- data.frame(COL_1 = c(1, 2, 3, 4),
COL_2 = c("A", "B", "C", "D"),
stringsAsFactors = FALSE)
datatable(data,
escape = FALSE,
filter = list(position = "top"),
rownames = FALSE)
})
observeEvent(input$disable, {
runjs("document.getElementById('table').getElementsByTagName('tbody')[0].setAttribute('onmousedown', 'event.stopPropagation();');")
})
observeEvent(input$enable, {
runjs("document.getElementById('table').getElementsByTagName('tbody')[0].removeAttribute('onmousedown');")
})
}
shinyApp(ui = ui, server = server)
Related
A similar question was posted but never answered: r shiny problem with datatable to copy a table with table head (colspan)
When running the below reproducible code, I'd like the DT "copy" button to include ALL table column and row headers, when there are multiple headers. So far DT copy only copies one header.
I have the code to do this using an action button/observeEvent() outside of DT (not shown in below code), but if possible I'd instead like to use DT's native copy clipboard function (like in the code below) because of other benefits it offers including but not limited to simplicity.
The images at the bottom better explain.
Maybe it's not possible. But maybe it is!
Reproducible code:
library(DT)
library(shiny)
library(dplyr)
library(htmltools)
library(data.table)
data <-
data.frame(
ID = c(1,1,1,2,2,2,3,3,3),
Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
Values = c(5, 10, 15, 0, 2, 4, 3, 6, 9),
State = c("X0","X1","X2","X0","X2","X0", "X2","X1","X9")
)
numTransit <- function(x, from=1, to=3){
setDT(x)
unique_state <- unique(x$State)
all_states <- setDT(expand.grid(list(from_state = unique_state, to_state = unique_state)))
dcast(x[, .(from_state = State[from],
to_state = State[to]),
by = ID]
[,.N, c("from_state", "to_state")]
[all_states,on = c("from_state", "to_state")],
to_state ~ from_state, value.var = "N"
)
}
ui <- fluidPage(
tags$head(tags$style(".datatables .display {margin-left: 0;}")),
h4(strong("Transition table inputs:")),
numericInput("transFrom", "From period:", 1, min = 1, max = 3),
numericInput("transTo", "To period:", 2, min = 1, max = 3),
h4(strong("Output transition table:")),
DTOutput("resultsDT"),
)
server <- function(input, output, session) {
results <-
reactive({
results <- numTransit(data, input$transFrom, input$transTo) %>%
replace(is.na(.), 0) %>%
bind_rows(summarise_all(., ~(if(is.numeric(.)) sum(.) else "Sum")))
results <- cbind(results, Sum = rowSums(results[,-1]))
})
output$data <- renderTable(data)
output$resultsDT <- renderDT(server=FALSE, {
datatable(
data = results(),
rownames = FALSE,
extensions = c("Buttons", "Select"), # for Copy button
selection = 'none', # for Copy button
filter = 'none',
container = tags$table(
class = 'display',
tags$thead(
tags$tr(
tags$th(rowspan = 2,sprintf('To state where end period = %s',input$transTo),style="border-right: solid 1px;"),
tags$th(colspan = 10,sprintf('From state where initial period = %s', input$transFrom))),
tags$tr(mapply(tags$th, colnames(results())[-1],
style = sprintf("border-right: solid %spx;", rep(0, ncol(results()) - 1L)),
SIMPLIFY = FALSE))
)
),
options = list(scrollX = F,
buttons = list(list(extend = "copy",text = 'Copy',exportOptions = list(modifier = list(selected = TRUE)))), # for Copy button
dom = 'Bft', # added 'B' for Copy button
lengthChange = T,
pagingType = "numbers",
autoWidth = T,
info = FALSE,
searching = FALSE)
) %>%formatStyle(c(1), `border-right` = "solid 1px")
})
}
shinyApp(ui, server)
Additional example:
Below is another, simpler example of trying to copy/paste all headers using DT, starting with the example used in post How to copy tableOutput to clipboard? (however adding the "sketch" container to datatable for a second column header to illustrate the copy/paste issue I'm trying to address):
library(shiny)
library(dplyr)
library(DT)
library(htmltools)
df <- mtcars
one <- function(.data, var, na = TRUE) {
return({
.data %>%
group_by(.data[[var]]) %>%
filter(!is.na(.data[[var]])) %>%
tally() %>%
mutate(`%` = 100*n/sum(n))
})
}
# ADDED SKETCH TO ORIGINAL EXAMPLE:
sketch = htmltools::withTags(table(
class = 'display',
thead(tr(th(colspan = 3, 'Table')),
tr(lapply(c('Variable','n','%'),th))
)
))
ui <- fluidPage(
selectInput("var", label = "Select Variable", choices = c("", names(df))),
DTOutput("valu", width = "15%")
)
server <- function(input, output) {
output$valu <- renderDT({
if(input$var != '') {
data <- df %>% one(input$var, na = input$check)
DT::datatable(data,
class = 'cell-border stripe',
rownames = FALSE,
extensions = c("Buttons", "Select"),
selection = 'none',
container = sketch, # ADDED SKETCH CONTAINER TO ORIGINAL EXAMPLE
options =
list(
select = TRUE,
dom = "Bt",
buttons = list(
list(
extend = "copy",
text = 'Copy'))
)) %>% formatStyle(
0,
target = "row",
fontWeight = styleEqual(1, "bold")
)
}
}, server = FALSE)
output$value <- renderTable({
if(input$var != '') {
data <- df %>% one(input$var, na = input$check)
return(data)
}
}, spacing = "xs", bordered = TRUE)
}
shinyApp(ui, server)
Hmm... for Copy I don't know yet. But you can export such a table to Excel and then copy from Excel. I agree this is not highly convenient, but I don't know another way. This requires some JS libraries:
tags$script(src = "xlsx.core.min.js"), # https://github.com/SheetJS/sheetjs/blob/master/dist/xlsx.core.min.js
tags$script(src = "FileSaver.min.js"), # https://raw.githubusercontent.com/eligrey/FileSaver.js/master/dist/FileSaver.min.js
tags$script(src = "tableexport.min.js"), # https://github.com/clarketm/TableExport/tree/master/dist
tags$link(rel = "stylesheet", href = "tableexport.min.css")
library(shiny)
library(DT)
library(shinyjs)
js_export <-
"
var $table = $('#DTtable').find('table');
var instance = $table.tableExport({
formats: ['xlsx'],
exportButtons: false,
filename: 'myTable',
sheetname: 'Sheet1'
});
var exportData0 = instance.getExportData();
var exportData = exportData0[Object.keys(exportData0)[0]]['xlsx'];
instance.export2file(exportData.data, exportData.mimeType, exportData.filename,
exportData.fileExtension, exportData.merges,
exportData.RTL, exportData.sheetname);
"
ui <- fluidPage(
useShinyjs(),
tags$head(
# put these files in the www subfolder
tags$script(src = "xlsx.core.min.js"),
tags$script(src = "FileSaver.min.js"),
tags$script(src = "tableexport.min.js")
),
DTOutput("DTtable"),
actionButton("export", "Export table", class = "btn-primary")
)
sketch <- htmltools::withTags(table(
class = "display",
thead(
tr(
th(rowspan = 2, "Species"),
th(colspan = 2, "Sepal"),
th(colspan = 2, "Petal")
),
tr(
lapply(rep(c("Length", "Width"), 2), th)
)
)
))
server <- function(input, output, session){
output[["DTtable"]] <- renderDT({
datatable(
head(iris, 6),
container = sketch, rownames = FALSE
) %>%
formatPercentage("Sepal.Length") %>%
formatCurrency("Sepal.Width")
})
observeEvent(input[["export"]], {
runjs(js_export)
})
}
shinyApp(ui, server)
Note that it also takes the formatting into account, but I'm wondering why there are some dates :-/
I have created the following shiny App
library(shiny)
library(rhandsontable)
ui <- fluidPage(
sidebarLayout(sidebarPanel = "Inputparameter",
selectInput(inputId = "Name", label = "Name", choices = c("A", "B", "C"))),
mainPanel (rHandsontableOutput(outputId = 'Adjusttable', width ='100%', height = 100%')))
server <- function(input, output, session) {
output$Adjusttable<-renderRHandsontable({
DF = data.frame(ID = 1:7,'Column2' = 0, Start = "D",FM="",stringsAsFactors = FALSE)
names(DF)[names(DF)=='Column2']<- input$Name
names(DF)[names(DF)=='FM']<-'FM'
DF$ID<-NULL
rhandsontable(DF, width = 280, height = 677,stretchH = "all") %>%
hot_col(col = "Start", type = "dropdown", source = c("Fw", "Sw"), fillHandle =
list(direction='vertical', autoInsertRow=TRUE))%>%
hot_context_menu(allowRowEdit = TRUE, allowColEdit = FALSE)
}, quoted = FALSE )}
shinyApp(ui, server)
The following results in an app with an editable table. When we fill values in the table,, and change the item in the name drop down, the values get reset to 0 and the table defaults to its default state. Is there a way to fill the table, change the number of rows, etc , change the name input and avoid resetting the table. I request someone to take a look.
Try this
library(shiny)
library(rhandsontable)
library(DT)
DF <- data.frame(ID = 1:7,Column2 = 0, Start = "D",FM="",stringsAsFactors = FALSE)
names(DF)[names(DF)=='FM']<-'FM'
DF$ID<-NULL
ui <- fluidPage(
sidebarLayout(
sidebarPanel( "Inputparameter",
selectInput(inputId = "Name", label = "Name", choices = c("A", "B", "C"))),
mainPanel( rHandsontableOutput(outputId = 'hot', width ='100%', height = '100%')
, DTOutput("t1")
)
)
)
server <- function(input, output, session) {
DF1 <- reactiveValues(data=DF)
observe({
input$Name
names(DF1$data)[1] <- input$Name
})
output$hot<-renderRHandsontable({
rhandsontable(DF1$data, width = 280, height = 677,stretchH = "all") %>%
hot_col(col = "Start", type = "dropdown", source = c("Fw", "Sw"), fillHandle =
list(direction='vertical', autoInsertRow=TRUE)) %>%
hot_context_menu(allowRowEdit = TRUE, allowColEdit = FALSE)
}, quoted = FALSE )
observe({
if (!is.null(input$hot)){
DF1$data <- (hot_to_r(input$hot))
}
})
output$t1 <- renderDT(DF1$data)
}
shinyApp(ui, server)
I'm trying to have an app with 3 dynamic filters where each filter is a subset of the previous.
I have partial success, however, since I have similar levels/factors for some of the data it seems this causing an issue with my filters outcome.
I can't seem to figure out how to solve the issue with the common levels for the "Spot" attribute.
Does anyone have any feedback?
Thanks!
My app:
library(rstudioapi)
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyjs)
library(shinyWidgets)
library(readxl)
library(DT)
library(devtools)
library(dplyr)
library(tidyr)
library(tidyverse)
library(rgl)
library(rglwidget)
col_1 <- c("A1","A1","A1", "A2", "A2", "B1", "B2", "C1","C1","C1")
col_2 <- c("a", "b", "c", "d", "e", "a", "b", "a", "b", "c")
col_3 <- c("Benz", "Audi", "Renault", "Ferrari", "Porsche", "Mercedes", "Benz", "Benz", "Audi", "Renault")
data_1 <- data.frame(col_1, col_2, col_3, stringsAsFactors = TRUE)
colnames(data_1) <- c("Building", "Spot", "Car")
server <- function(input, output, session) {
filterCars <- reactive({
filterCar <- data_1
filterCar <- droplevels.data.frame(filterCar)
return(filterCar)
})
filterBuilding <- reactive({
unique(as.character(filterCars()$Building))
})
output$filterBuilding <- renderUI({
pickerInput(inputId = 'filter_Building', 'Building',
choices = sort(filterBuilding()),
multiple = TRUE,
width = "1250px",
options = list(`actions-box` = TRUE),
selected = sort(as.character(filterCars()$Building)))
})
# # Subset dynamically the previous reactive filter #
datasub1 <- reactive({
data_1[data_1$Building == input$filter_Building,]
})
filterSpot <- reactive({
unique(as.character(datasub1()$Spot))
})
output$filterSpot <- renderUI({
pickerInput(inputId = 'filter_Spot', 'Spot',
choices = sort(filterSpot()),
multiple=TRUE,
width = "1250px",
options = list(`actions-box` = TRUE),
selected = sort(as.character(filterCars()$Spot)))
})
# Subset dynamically the previous reactive filter #
datasub2 <- reactive({
data_1[data_1$Spot == input$filter_Spot,]
})
filterBrand <- reactive({
unique(as.character(datasub2()$Car))
})
output$filterBrand <- renderUI({
pickerInput(inputId = 'filter_Brand', 'ID',
choices = sort(filterBrand()),
multiple = TRUE,
width = "1250px",
selected = NULL,
options = list("max-options" = 4, `actions-box` = TRUE))
})
output$databaseCars <- DT::renderDT({
# Subset for plotly reactivity
Filter1 <- droplevels.data.frame(data_1)
Filter2 <- filter(Filter1,
Filter1$Building %in% input$filter_Building,
Filter1$Spot %in% input$filter_Spot,
Filter1$Car %in% input$filter_Brand)
# Plot
datatable(Filter2,
filter="none",
selection="none",
escape=FALSE,
rownames = FALSE,
# colnames = c("", ""),
autoHideNavigation = TRUE,
style = 'bootstrap4',
options = list(searching = FALSE, # remove search option
ordering = FALSE, # remove sort option
paging = FALSE, # remove paging
info = FALSE # remove bottom information
)) %>%
formatStyle(columns = 1, fontWeight = 'bold', `text-align` = 'left') # text to bold and lign left in first column
})
}
# User Interface
ui <- fluidPage(
mainPanel(
fluidRow(
column(12,
uiOutput("filterBuilding")
)),
fluidRow(
column(12,
uiOutput("filterSpot")
)),
fluidRow(
column(12,
uiOutput("filterBrand")
)),
p(DTOutput('databaseCars'))
)
)
shinyApp(ui, server)
A few issues I've spotted:
you can have several factors/selections per variable, therefore you need to use %in% instead of == for the filtering
for the brands, you've set selected = NULL, therefore no brand was selected by default
in general, it is recommend to create the UI elements in the ui part and update them with updatePickerInput instead of using renderUI, because then all rendering has to be done server side, which can slow the app down (especially if you have a several parallel users, as it is only served by one R process
Here is my take:
library(shiny)
library(DT)
library(dplyr)
library(shinyWidgets)
col_1 <- c("A1","A1","A1", "A2", "A2", "B1", "B2", "C1","C1","C1")
col_2 <- c("a", "b", "c", "d", "e", "a", "b", "a", "b", "c")
col_3 <- c("Benz", "Audi", "Renault", "Ferrari", "Porsche", "Mercedes", "Benz", "Benz", "Audi", "Renault")
data_1 <- data.frame(col_1, col_2, col_3, stringsAsFactors = TRUE)
colnames(data_1) <- c("Building", "Spot", "Car")
server <- function(input, output, session) {
filterCars <- reactive({
filterCar <- data_1
filterCar <- droplevels.data.frame(filterCar)
return(filterCar)
})
filterBuilding <- reactive({
unique(as.character(filterCars()$Building))
})
observeEvent(filterBuilding(), {
updatePickerInput(session,
"filter_Building",
choices = filterBuilding(),
selected = sort(filterBuilding()))
})
# # Subset dynamically the previous reactive filter #
datasub1 <- reactive({
data_1[data_1$Building %in% input$filter_Building,]
})
filterSpot <- reactive({
unique(as.character(datasub1()$Spot))
})
observeEvent(filterSpot(), {
updatePickerInput(session,
"filter_Spot",
choices = sort(filterSpot()),
selected = sort(filterSpot()))
})
# Subset dynamically the previous reactive filter #
datasub2 <- reactive({
# browser()
data_1[data_1$Spot %in% input$filter_Spot,]
})
filterBrand <- reactive({
unique(as.character(datasub2()$Car))
})
observeEvent(filterBrand(), {
updatePickerInput(session,
"filter_Brand",
choices = sort(filterBrand()),
selected = sort(filterBrand()))
})
output$databaseCars <- DT::renderDT({
# Subset for plotly reactivity
Filter1 <- droplevels.data.frame(data_1)
Filter2 <- filter(Filter1,
Filter1$Building %in% input$filter_Building,
Filter1$Spot %in% input$filter_Spot,
Filter1$Car %in% input$filter_Brand)
# Plot
datatable(Filter2,
filter="none",
selection="none",
escape=FALSE,
rownames = FALSE,
# colnames = c("", ""),
autoHideNavigation = TRUE,
style = 'bootstrap4',
options = list(searching = FALSE, # remove search option
ordering = FALSE, # remove sort option
paging = FALSE, # remove paging
info = FALSE # remove bottom information
)) %>%
formatStyle(columns = 1, fontWeight = 'bold', `text-align` = 'left') # text to bold and lign left in first column
})
}
# User Interface
ui <- fluidPage(
mainPanel(
fluidRow(
column(12,
pickerInput(inputId = 'filter_Building', 'Building',
choices = NULL,
multiple = TRUE,
width = "1250px",
options = list(`actions-box` = TRUE),
selected = NULL)
)),
fluidRow(
column(12,
pickerInput(inputId = 'filter_Spot', 'Spot',
choices = NULL,
multiple=TRUE,
width = "1250px",
options = list(`actions-box` = TRUE),
selected = NULL)
)),
fluidRow(
column(12,
pickerInput(inputId = 'filter_Brand', 'ID',
choices = NULL,
multiple = TRUE,
width = "1250px",
selected = NULL,
options = list("max-options" = 4, `actions-box` = TRUE))
)),
p(DTOutput('databaseCars'))
)
)
shinyApp(ui, server)
I want to create a tabsetPanel that displays a selection of dataframes based on a selectizeInput, while also allowing for permanent edits of the data. I use editable DataTables to render the dataframes but couldn't find a way to save the edits. This example code illustrates my problem:
library(shiny)
library(shinyWidgets)
library(shinyjs)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId = "dataframes", label = "select dataframes",
choices = c("iris", "mtcars", "DNase", "ChickWeight"), multiple = TRUE, options = list(create = T))
),
mainPanel(
uiOutput("dataframes_rendered")
)
)
)
server <- function(input, output) {
output$dataframes_rendered = renderUI({
# create one tab per df
tabs = lapply(input$dataframes, function(df){
output[[df]] = DT::renderDT(get(df), editable = T, rownames = F, options = list(dom = "t"))
tabPanel(title = df, value = NULL, dataTableOutput(outputId = df), br())
})
# create tabsetPanel
do.call(tabsetPanel, c(tabs, id = "df_tabset"))
})
}
shinyApp(ui = ui, server = server)
I understand why the edits are not saved in my example (the dataframes are re-rendered with every change in the selectizeInput) but, so far, everything I tried to to save the edits and re-render the editeed tables did not work.
Please try the below:
library(shiny)
library(shinyWidgets)
library(shinyjs)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId = "dataframes", label = "select dataframes",
choices = c("iris", "mtcars", "DNase", "ChickWeight"), multiple = TRUE, options = list(create = T))
),
mainPanel(
tabsetPanel(id = "df_tabset")
)
)
)
server <- function(input, output, session) {
tables <- reactiveValues(
iris = iris,
mtcars = mtcars,
DNase = DNase,
ChickWeight = ChickWeight,
df_tabset = NULL
)
observeEvent(input$dataframes, {
if (length(input$dataframes) > length(tables$df_tabset)) {
df = input$dataframes[! input$dataframes %in% tables$df_tabset]
output[[df]] = renderDT(tables[[df]], editable = T, rownames = F, options = list(dom = "t"))
appendTab(inputId = "df_tabset", select = TRUE,
tabPanel(title = df, value = df, DTOutput(outputId = df))
)
tables$df_tabset = input$dataframes
} else {
df = tables$df_tabset[! tables$df_tabset %in% input$dataframes]
removeTab(inputId = "df_tabset", target = df)
tables$df_tabset = input$dataframes
}
}, ignoreNULL = FALSE, ignoreInit = TRUE)
observeEvent(input$iris_cell_edit, {
tables$iris[input$iris_cell_edit$row, input$iris_cell_edit$col + 1] = input$iris_cell_edit$value
})
observeEvent(input$mtcars_cell_edit, {
tables$mtcars[input$mtcars_cell_edit$row, input$mtcars_cell_edit$col + 1] = input$mtcars_cell_edit$value
})
observeEvent(input$DNase_cell_edit, {
tables$DNase[input$DNase_cell_edit$row, input$DNase_cell_edit$col + 1] = input$DNase_cell_edit$value
})
observeEvent(input$ChickWeight_cell_edit, {
tables$ChickWeight[input$ChickWeight_cell_edit$row, input$ChickWeight_cell_edit$col + 1] = input$ChickWeight_cell_edit$value
})
}
shinyApp(ui = ui, server = server)
I also made a change to your code by adding and removing tabs rather than rerendering all of them each time.
The select = TRUE takes you to the added tab but this can be changed to the default of FALSE to remain on the current tab.
The main way of saving changes is to use reactives/reactiveValues. See DT Shiny and examples.
Update
Based on the comment below, I now create each observeEvent() as needed.
library(shiny)
library(shinyWidgets)
library(shinyjs)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId = "dataframes", label = "select dataframes",
choices = c("iris", "mtcars", "DNase", "ChickWeight"), multiple = TRUE, options = list(create = T))
),
mainPanel(
tabsetPanel(id = "df_tabset")
)
)
)
server <- function(input, output, session) {
tables <- reactiveValues(
iris = iris,
mtcars = mtcars,
DNase = DNase,
ChickWeight = ChickWeight,
df_tabset = NULL
)
observeEvent(input$dataframes, {
if (length(input$dataframes) > length(tables$df_tabset)) {
df = input$dataframes[! input$dataframes %in% tables$df_tabset]
output[[df]] = renderDT(tables[[df]], editable = T, rownames = F, options = list(dom = "t"))
appendTab(inputId = "df_tabset", select = TRUE,
tabPanel(title = df, value = df, DTOutput(outputId = df))
)
observeEvent(input[[paste0(df, '_cell_edit')]], {
tables[[df]][input[[paste0(df, '_cell_edit')]]$row, input[[paste0(df, '_cell_edit')]]$col + 1] = input[[paste0(df, '_cell_edit')]]$value
})
tables$df_tabset = input$dataframes
} else {
df = tables$df_tabset[! tables$df_tabset %in% input$dataframes]
removeTab(inputId = "df_tabset", target = df)
tables$df_tabset = input$dataframes
}
}, ignoreNULL = FALSE, ignoreInit = TRUE)
}
shinyApp(ui = ui, server = server)
What I am attempting to do, is to allow the user to pass in a configuration/lookup excel table into shiny, display this table in shiny, allow the user to make cells edits in shiny, and use the values that were edited from the editable table for calculations. My problem arises for the last step "use the values that were edited from the editable table for calculations".
The excel file consists of 2 tabs with data of the following content:
Tab1 Name: "parameters"
data.frame(Name = c("a", "b", "c"), Value = c(1:3))
Tab2 Name: "parameters2"
data.frame(Name = c("a", "b", "c"), Value = c(4:6))
The ideal shiny app would do the following:
1) At upload, perform a calculation adding the unchanged first values of Tab 1 and Tab 2. This would be 1 + 4 = 5.
2) If user edits Tab 1's value of 1 to 8, then the calculation would result in 8 + 4 = 12.
Effectively, I want to use the edited tables values to update all my calculations if the user makes any edits to it. I know this can be done by simply uploading a new file in shiny, but I would rather allow them to do this in shiny as opposed to uploading a new file.
Here is my shiny app. Appreciate any help/guidance!
library(shiny)
library(DT)
shinyApp(
ui <- fluidPage(
fileInput(inputId = "config", label = "Upload Configuration File",
multiple = F, accept = c(".xlsx", ".xls")),
verbatimTextOutput("txt"),
tagList(tags$head(tags$style(type = 'text/css','.navbar-brand{display:none;}')),
navbarPage(title = "",
tabPanel(title = "Parameters",
dataTableOutput(outputId = "edit.param", width = 2)),
tabPanel(title = "Parameters2",
dataTableOutput(outputId = "edit.param2", width = 2))
)
)
),
server = function(input, output, session) {
config.path = reactive({
inFile = input$config
if(is.null(inFile)) {
return(NULL)
} else {
return(inFile$datapath)
}
})
df.param = reactive({
read_excel(path = config.path(), sheet = "parameters")
})
df.param2 = reactive({
read_excel(path = config.path(), sheet = "parameters2")
})
output$edit.param = renderDT(df.param(), selection = "none", server = F, editable = "cell")
output$edit.param2 = renderDT(df.param2(), selection = "none", server = F, editable = "cell")
observeEvent(input$edit.param_cell_edit, {
df.param()[input$edit.param_cell_edit$row, input$edit.param_cell_edit$col] <<- input$edit.param_cell_edit$value
})
observeEvent(input$edit.param2_cell_edit, {
df.param()[input$edit.param2_cell_edit$row, input$edit.param2_cell_edit$col] <<- input$edit.param2_cell_edit$value
})
output$txt = reactive({
df.param()$value[1] + df.param2()$value[1]
})
}
)
I also tried this for the server section and had no luck either:
output$edit.param = renderDT(df.param(), selection = "none", server = F, editable = "cell")
output$edit.param2 = renderDT(df.param2(), selection = "none", server = F, editable = "cell")
observe(input$edit.param_cell_edit)
observe(input$edit.param2_cell_edit)
Could you try this? (I have not tried).
library(shiny)
library(DT)
shinyApp(
ui <- fluidPage(
fileInput(inputId = "config", label = "Upload Configuration File",
multiple = F, accept = c(".xlsx", ".xls")),
verbatimTextOutput("txt"),
tagList(tags$head(tags$style(type = 'text/css','.navbar-brand{display:none;}')),
navbarPage(title = "",
tabPanel(title = "Parameters",
dataTableOutput(outputId = "edit_param", width = 2)),
tabPanel(title = "Parameters2",
dataTableOutput(outputId = "edit_param2", width = 2))
)
)
),
server = function(input, output, session) {
config.path = reactive({
inFile = input$config
if(is.null(inFile)) {
return(NULL)
} else {
return(inFile$datapath)
}
})
df_param <- reactiveVal()
observe({
req(config.path())
df_param(read_excel(path = config.path(), sheet = "parameters"))
})
df_param2 <- reactiveVal()
observe({
req(config.path())
df_param2(read_excel(path = config.path(), sheet = "parameters2"))
})
output$edit_param = renderDT({
req(df_param())
datatable(isolate(df_param()), selection = "none", editable = "cell")
})
output$edit_param2 = renderDT({
req(df_param2())
datatable(isolate(df_param2()), selection = "none", editable = "cell")
})
proxy <- dataTableProxy("edit_param")
proxy2 <- dataTableProxy("edit_param2")
observeEvent(input$edit_param_cell_edit, {
info <- input$edit_param_cell_edit
df_param(editData(df_param(), info, proxy, resetPaging = FALSE))
})
observeEvent(input$edit_param2_cell_edit, {
info <- input$edit_param2_cell_edit
df_param2(editData(df_param2(), info, proxy2, resetPaging = FALSE))
})
output$txt = renderPrint({
df_param()$value[1] + df_param2()$value[1]
})
}
)