我想构建一个闪亮的应用程序,为用户提供一个选择器(用于子集数据)、一个数据表(用于显示数据)和一组按钮,可用于标记数据表中当前选定的行(请参阅代表如下)。重要的是,数据表应该是隔离的(即,当底层数据被修改时,它不应该更新和重新渲染),以避免重置过滤器/排序/页面或由于重新加载而闪烁。但是,我认为我错误地使用了isolate()或DataTableProxy()(参见下面的示例)。最终我想保存编辑后的数据,这是另一个故事,但我什至很难让它正确显示:
在下面的示例中,我实现了我的预期,除了 DataTable 不是隔离的,并且当我选择一行并按下“Seen X times”按钮时会重新呈现。
library(shiny)
library(shinydashboard)
library(shinyjs)
library(dplyr)
library(DT)
library(stringr)
library(data.table)
test_data <- mtcars
test_data$manufacturer <- str_split_fixed(rownames(test_data), " ", 2)[,1]
test_data$model <- str_split_fixed(rownames(test_data), " ", 2)[,2]
test_data$seen <- ""
ui <- dashboardPage(
dashboardHeader(title = "mtcars seen x times", titleWidth = 450),
dashboardSidebar(width = 300,
sidebarMenu(
id = "menu1",
menuItem("Cars", tabName = "Cars"),
selectInput("selData","Manufacturer",choices = test_data$manufacturer)
)
),
dashboardBody(
useShinyjs(),
tabItems(
tabItem(
tabName = "Cars",
fluidRow(
box(title = "details",
width = 12, style='overflow-y:scroll; height:80vh;',
# create array of buttons
actionButton("seen_1","Seen 1x"),
actionButton("seen_2","Seen 2x"),
actionButton("seen_3","Seen 3x"),
actionButton("seen_4","Seen 4x"),
actionButton("seen_5","Seen 5x"),
DT::dataTableOutput("details"))
)
)
)
)
)
server <- function(input, output, session) {
test_data_current <- reactiveVal()
test_data_selection <- reactiveVal()
observeEvent(input$selData, {
test_data_current(test_data[test_data$manufacturer == input$selData,])
test_data_selection(test_data[test_data$manufacturer == input$selData, c("manufacturer","model","mpg","disp","hp","seen")])
})
output$details <- DT::renderDataTable({
input$selData
DT::datatable(test_data_selection(),
rownames = F,
selection = "single")
})
# update data based on which button was pressed
proxy = dataTableProxy("details")
lapply(1:5, function(i){
selector <- paste0("seen_",i)
observeEvent(input[[selector]], {
req(input$details_rows_selected)
info <- data.frame("row" = input$details_rows_selected,
"col" = which(colnames(test_data_selection()) == "seen"),
value = recode(i, "1" = "1x", "2" = "2x", "3" = "3x", "4" = "4x", "5" = "5x"))
edit_data <<- editData(test_data_selection(), info, proxy)
test_data_selection(edit_data)
})
})
}
shinyApp(ui = ui, server = server)
在调整后的示例中,我使用isolate()中包含的数据调用renderDataTable。
library(shiny)
library(shinydashboard)
library(shinyjs)
library(dplyr)
library(DT)
library(stringr)
library(data.table)
test_data <- mtcars
test_data$manufacturer <- str_split_fixed(rownames(test_data), " ", 2)[,1]
test_data$model <- str_split_fixed(rownames(test_data), " ", 2)[,2]
test_data$seen <- ""
ui <- dashboardPage(
dashboardHeader(title = "mtcars seen x times", titleWidth = 450),
dashboardSidebar(width = 300,
sidebarMenu(
id = "menu1",
menuItem("Cars", tabName = "Cars"),
selectInput("selData","Manufacturer",choices = test_data$manufacturer)
)
),
dashboardBody(
useShinyjs(),
tabItems(
tabItem(
tabName = "Cars",
fluidRow(
box(title = "details",
width = 12, style='overflow-y:scroll; height:80vh;',
# create array of buttons
actionButton("seen_1","Seen 1x"),
actionButton("seen_2","Seen 2x"),
actionButton("seen_3","Seen 3x"),
actionButton("seen_4","Seen 4x"),
actionButton("seen_5","Seen 5x"),
DT::dataTableOutput("details"))
)
)
)
)
)
server <- function(input, output, session) {
test_data_current <- reactiveVal()
test_data_selection <- reactiveVal()
observeEvent(input$selData, {
test_data_current(test_data[test_data$manufacturer == input$selData,])
test_data_selection(test_data[test_data$manufacturer == input$selData, c("manufacturer","model","mpg","disp","hp","seen")])
})
output$details <- DT::renderDataTable({
input$selData
DT::datatable(isolate(test_data_selection()),
rownames = F,
selection = "single")
})
# update data based on which button was pressed
proxy = dataTableProxy("details")
lapply(1:5, function(i){
selector <- paste0("seen_",i)
observeEvent(input[[selector]], {
req(input$details_rows_selected)
info <- data.frame("row" = input$details_rows_selected,
"col" = which(colnames(test_data_selection()) == "seen"),
value = recode(i, "1" = "1x", "2" = "2x", "3" = "3x", "4" = "4x", "5" = "5x"))
edit_data <<- editData(test_data_selection(), info, proxy)
test_data_selection(edit_data)
})
})
}
shinyApp(ui = ui, server = server)
但是,这会导致意外的行为:如果我按下按钮,所有行都会消失,直到我更改 input$selData。我怀疑我使用的isolate 或DataTableProxy 是错误的,但我似乎无法弄清楚。我真的很感谢任何帮助。
答案:在此设置中,使用 rownames = F 调用 datatable() 会导致所描述的意外行为。以下代码按预期执行:
library(shiny)
library(shinydashboard)
library(shinyjs)
library(dplyr)
library(DT)
library(stringr)
library(data.table)
test_data <- mtcars
test_data$manufacturer <- str_split_fixed(rownames(test_data), " ", 2)[,1]
test_data$model <- str_split_fixed(rownames(test_data), " ", 2)[,2]
test_data$seen <- ""
ui <- dashboardPage(
dashboardHeader(title = "mtcars seen x times", titleWidth = 450),
dashboardSidebar(width = 300,
sidebarMenu(
id = "menu1",
menuItem("Cars", tabName = "Cars"),
selectInput("selData","Manufacturer",choices = test_data$manufacturer)
)
),
dashboardBody(
useShinyjs(),
tabItems(
tabItem(
tabName = "Cars",
fluidRow(
box(title = "details",
width = 12, style='overflow-y:scroll; height:80vh;',
# create array of buttons
actionButton("seen_1","Seen 1x"),
actionButton("seen_2","Seen 2x"),
actionButton("seen_3","Seen 3x"),
actionButton("seen_4","Seen 4x"),
actionButton("seen_5","Seen 5x"),
DT::dataTableOutput("details"))
)
)
)
)
)
server <- function(input, output, session) {
test_data_current <- reactiveVal()
test_data_selection <- reactiveVal()
observeEvent(input$selData, {
test_data_current(test_data[test_data$manufacturer == input$selData,])
test_data_selection(test_data[test_data$manufacturer == input$selData, c("manufacturer","model","mpg","disp","hp","seen")])
})
output$details <- DT::renderDataTable({
input$selData
DT::datatable(isolate(test_data_selection()),
selection = "single")
})
# update data based on which button was pressed
proxy = dataTableProxy("details")
lapply(1:5, function(i){
selector <- paste0("seen_",i)
observeEvent(input[[selector]], {
req(input$details_rows_selected)
info <- data.frame("row" = input$details_rows_selected,
"col" = which(colnames(test_data_selection()) == "seen"),
value = recode(i, "1" = "1x", "2" = "2x", "3" = "3x", "4" = "4x", "5" = "5x"))
edit_data <<- editData(test_data_selection(), info, proxy)
test_data_selection(edit_data)
})
})
}
shinyApp(ui = ui, server = server)