I have some data in a dataframe. I can display the dataframe as a table with DataTables.
However, I would like to display the data as a grid with N columns, so that every N rows from the data frame are shown in the same row.
Show data as a grid:
As shown in the image above, I have gotten a grid to show up by using HTML to render the data frame directly. But the next step is where I am stuck, which is I want to be able to show a modal dialog when a cell in the grid is clicked. I have that working in the data table, but I haven't been able to figure out how to make a div clickable, such that when handling the event I know which cell was clicked?
library("shiny")
library("tidyr")
library("tidyverse")
library("dplyr")
library("shinydashboard")
# generate html grid from data frame
getHTML <- function (frames) {
innerhtml = '<div class="grid-container">'
for (row in 1:(nrow(frames))) {
id <- frames[row, "id"]
name <- frames[row, "names"]
row_html = '<div class="grid-item">'
row_html = paste(row_html, '<span>Name: ' , name, "id ", row , '</span>')
row_html = paste(row_html, '</div>')
innerhtml = paste(innerhtml, row_html)
}
paste(innerhtml, "</div>")
return (innerhtml)
}
#show modal dialog for player id and name
plotModal <- function(id, name) {
modalDialog(
p(paste("Player # ", id, ", " , name,", was clicked")),
title = paste("Player " , id),
easyClose = TRUE
)
}
ui <- dashboardPage(
# Application title
dashboardHeader(title = "Dashboard"),
dashboardSidebar(
h3("Filters")
),
dashboardBody(
tags$head(tags$style(HTML('
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
}'))),
fluidRow(
box(title="Render as table", column(width=12, DT::dataTableOutput("player_table"))),
box(title="Render as Grid", column(width=12, uiOutput("player_grid")))
)
)
)
server <- function(input, output, session) {
# data to be rendered
frames = data.frame(names= c("james","kyle", "sally","hannah","jeff","kurt"), ids=c(1:6))
output$player_table <- DT::renderDataTable({
DT::datatable(frames, rownames=FALSE, selection = 'single')
})
#when a row in the table is clicked, show popup
observeEvent(input$player_table_cell_clicked, {
info = input$player_table_cell_clicked
# do nothing if not clicked yet, or the clicked cell is not in the 1st column
if (is.null(info$value)) {
return()
}
row = frames[info$row, ]
showModal(plotModal(row$id, row$names))
})
output$player_grid <- renderUI ({
HTML(getHTML(frames))
})
}
# Create Shiny app ----
shinyApp(ui, server,options=list(host="0.0.0.0", port=8015))
Here is a way:
library(shiny)
library(shinydashboard)
js <- "
$(document).ready(function(){
$('body').on('click', '.grid-item span', function(){
var name = $(this).data('name'),
id = $(this).data('id');
Shiny.setInputValue('cell', {name: name, id: id});
});
});
"
# generate html grid from data frame
getHTML <- function (frames) {
innerhtml = '<div class="grid-container">'
for (row in 1:(nrow(frames))) {
id <- frames[row, "ids"]
name <- frames[row, "names"]
row_html = '<div class="grid-item">'
cell <- sprintf("<span data-name='%s' data-id='%s'>Name: %s - id: %s</span>",
name, id, name, id)
row_html = paste(row_html, cell)
row_html = paste(row_html, '</div>')
innerhtml = paste(innerhtml, row_html)
}
paste(innerhtml, "</div>")
return (innerhtml)
}
#show modal dialog for player id and name
plotModal <- function(id, name) {
modalDialog(
p(paste("Player # ", id, ", " , name,", was clicked")),
title = paste("Player " , id),
easyClose = TRUE
)
}
ui <- dashboardPage(
# Application title
dashboardHeader(title = "Dashboard"),
dashboardSidebar(
h3("Filters")
),
dashboardBody(
tags$head(tags$style(HTML('
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
}')),
tags$script(HTML(js))),
fluidRow(
box(title="Render as Grid", column(width=12, uiOutput("player_grid")))
)
)
)
server <- function(input, output, session) {
# data to be rendered
frames = data.frame(
names= c("james","kyle", "sally","hannah","jeff","kurt"),
ids=c(1:6)
)
#when a row in the table is clicked, show popup
observeEvent(input$cell, {
showModal(plotModal(input$cell$id, input$cell$name))
})
output$player_grid <- renderUI ({
HTML(getHTML(frames))
})
}
# Create Shiny app ----
shinyApp(ui, server)