Bookmarking and Saving the Bookmarks in R Shiny

Bookmarking and saving the bookmarks in R shiny

Here is an alternative approach to my earlier answer using saveRDS() instead of sqlite:

Edit: Added username check.

library(shiny)
# library(RSQLite)
library(data.table)

ui <- function(request) {
fluidPage(
plotOutput("plot"),
sliderInput("n", "Number of observations", 1, nrow(faithful), 100),
fluidRow(column(2, textInput(inputId = "description", label = "Bookmark description", placeholder = "Data Summary")), column(2, bookmarkButton(id="bookmarkBtn"))),
DT::dataTableOutput("urlTable", width = "100%"),
tags$style(type='text/css', "#bookmarkBtn { width:100%; margin-top: 25px;}")
)
}

server <- function(input, output, session) {

# con <- dbConnect(RSQLite::SQLite(), "bookmarks.db", overwrite = FALSE)
myBookmarks <- reactiveValues(urlDF = NULL)

observeEvent(input$bookmarkBtn, {
session$doBookmark()
})

# if(dbExistsTable(con, "Bookmarks")){
# tmpUrlDF <- data.table(dbReadTable(con, "Bookmarks"))
# myBookmarks$urlDF <- tmpUrlDF[, Timestamp := as.POSIXct(Timestamp, origin="1970-01-01 00:00")]
# } else {
# myBookmarks$urlDF <- NULL
# }

if(file.exists("bookmarks.rds")){
myBookmarks$urlDF <- readRDS("bookmarks.rds")
} else {
myBookmarks$urlDF <- NULL
}

session$onSessionEnded(function() {
tmpUrlDF <- isolate({myBookmarks$urlDF})
if(!is.null(tmpUrlDF)){
# dbWriteTable(con, "Bookmarks", tmpUrlDF, overwrite = TRUE)
saveRDS(tmpUrlDF, "bookmarks.rds")
}
# dbDisconnect(con)
})

setBookmarkExclude(c("bookmarkBtn", "description", "urlTable_cell_clicked", "urlTable_rows_all", "urlTable_rows_current", "urlTable_rows_selected", "urlTable_search", "urlTable_state", "urlTable_row_last_clicked"))

output$plot <- renderPlot({
hist(faithful$eruptions[seq_len(input$n)], breaks = 40)
})

onBookmarked(fun=function(url){
if(!url %in% myBookmarks$urlDF$URL){
if(is.null(myBookmarks$urlDF)){
myBookmarks$urlDF <- unique(data.table(Description = input$description, URL = paste0("<a href='", url, "'>", url,"</a>"), Timestamp = Sys.time(), Session = session$token, User = Sys.getenv("USERNAME")), by="URL")
} else {
myBookmarks$urlDF <- unique(rbindlist(list(myBookmarks$urlDF, data.table(Description = input$description, URL = paste0("<a href='", url, "'>", url,"</a>"), Timestamp = Sys.time(), Session = session$token, User = Sys.getenv("USERNAME")))), by="URL")
}
}
})

output$urlTable = DT::renderDataTable({
req(myBookmarks$urlDF)
myBookmarks$urlDF[User %in% Sys.getenv("USERNAME")]
}, escape=FALSE)

}

enableBookmarking(store = "url")
shinyApp(ui, server)

Save R Shiny bookmark into a table and retrieve it

Here is how I would approach this:

Edit: Now using sqlite to persist the changes across different session, also duplicates are avoided.

2nd Edit: Added a description input for the bookmarks.

library(shiny)
library(RSQLite)
library(data.table)

ui <- function(request) {
fluidPage(
plotOutput("plot"),
sliderInput("n", "Number of observations", 1, nrow(faithful), 100),
fluidRow(column(2, textInput(inputId = "description", label = "Bookmark description", placeholder = "Data Summary")), column(2, bookmarkButton(id="bookmarkBtn"))),
DT::dataTableOutput("urlTable", width = "100%"),
tags$style(type='text/css', "#bookmarkBtn { width:100%; margin-top: 25px;}")
)
}

server <- function(input, output, session) {

con <- dbConnect(RSQLite::SQLite(), "bookmarks.db", overwrite = FALSE)
myBookmarks <- reactiveValues(urlDF = NULL)

observeEvent(input$bookmarkBtn, {
session$doBookmark()
})

if(dbExistsTable(con, "Bookmarks")){
tmpUrlDF <- data.table(dbReadTable(con, "Bookmarks"))
myBookmarks$urlDF <- tmpUrlDF[, Timestamp := as.POSIXct(Timestamp, origin="1970-01-01 00:00")]
} else {
myBookmarks$urlDF <- NULL
}

session$onSessionEnded(function() {
tmpUrlDF <- isolate({myBookmarks$urlDF})
if(!is.null(tmpUrlDF)){
dbWriteTable(con, "Bookmarks", tmpUrlDF, overwrite = TRUE)
}
dbDisconnect(con)
})

setBookmarkExclude(c("bookmarkBtn", "description", "urlTable_cell_clicked", "urlTable_rows_all", "urlTable_rows_current", "urlTable_rows_selected", "urlTable_search", "urlTable_state", "urlTable_row_last_clicked"))

output$plot <- renderPlot({
hist(faithful$eruptions[seq_len(input$n)], breaks = 40)
})

onBookmarked(fun=function(url){
if(!url %in% myBookmarks$urlDF$URL){
if(is.null(myBookmarks$urlDF)){
myBookmarks$urlDF <- unique(data.table(Description = input$description, URL = paste0("<a href='", url, "'>", url,"</a>"), Timestamp = Sys.time(), Session = session$token), by="URL")
} else {
myBookmarks$urlDF <- unique(rbindlist(list(myBookmarks$urlDF, data.table(Description = input$description, URL = paste0("<a href='", url, "'>", url,"</a>"), Timestamp = Sys.time(), Session = session$token))), by="URL")
}
}
})

output$urlTable = DT::renderDataTable({
myBookmarks$urlDF
}, escape=FALSE)

}

enableBookmarking(store = "url")
shinyApp(ui, server)

You might want to think about saving the bookmark table to a file (e.g. using saveRDS()) and load it globally so its available for new sessions.
For further information see this.

URL Bookmarking R Shiny

According to your description I guess for more complex apps you are hitting a browser limit with the encoded state URLs as mentioned in this article:

With an encoded state, the URL could become very long if there are many values. Some browsers have a limit of about 2,000 characters for the length of a URL, so if the bookmark URL is longer than that, it will not work properly in those browsers.

Therefore you should start using saved-to-server bookmarks by setting

enableBookmarking(store = "server")

Instead of:

enableBookmarking(store = "url")

Edit: Also for this to work your UI code must be wrapped in a function taking request as an argument:

2nd Edit: Added id = "myNavbarPage" to the navbarPage - so it will be recognized as an input for bookmarking (and restored accordingly).

library(shiny)
library(ggplot2)
library(DT)
library(shinyjqui)
library(shinydashboard)
library(shinydashboardPlus)
library(data.table)

ui <- function(request) {navbarPage(
"Navbar!", id = "myNavbarPage",
tabPanel("Plot",
sidebarLayout(
sidebarPanel(radioButtons(
"plotType", "Plot type",
c("Scatter" = "p", "Line" = "l")
)),
mainPanel(plotOutput("plot"))
)),
tabPanel(
"Summary",
fluidPage(
plotOutput("bookmarkplot"),
sliderInput("n", "Number of observations", 1, nrow(faithful), 100),
fluidRow(column(
2,
textInput(
inputId = "description",
label = "Bookmark description",
placeholder = "Data Summary"
)
), column(2, bookmarkButton(id = "bookmarkBtn"))),
DT::dataTableOutput("urlTable", width = "100%"),
tags$style(type = 'text/css', "#bookmarkBtn { width:100%; margin-top: 25px;}")
)
),
navbarMenu(
"More",
tabPanel("Table",
DT::dataTableOutput("table")),
tabPanel("About",
fluidRow(column(
3,
img(
class = "img-polaroid",
src = paste0(
"http://upload.wikimedia.org/",
"wikipedia/commons/9/92/",
"1919_Ford_Model_T_Highboy_Coupe.jpg"
)
),
tags$small(
"Source: Photographed at the Bay State Antique ",
"Automobile Club's July 10, 2005 show at the ",
"Endicott Estate in Dedham, MA by ",
a(href = "http://commons.wikimedia.org/wiki/User:Sfoskett",
"User:Sfoskett")
)
)))
)
)}

server <- function(input, output, session) {
output$plot <- renderPlot({
plot(cars, type = input$plotType)
})

output$summary <- renderPrint({
summary(cars)
})

output$table <- DT::renderDataTable({
DT::datatable(cars)
})

#BOOKMARK AND SAVING THEM
myBookmarks <- reactiveValues(urlDF = NULL)
observeEvent(input$bookmarkBtn, {
session$doBookmark()
})

if (file.exists("bookmarks.rds")) {
myBookmarks$urlDF <- readRDS("bookmarks.rds")
} else {
myBookmarks$urlDF <- NULL
}

session$onSessionEnded(function() {
tmpUrlDF <- isolate({
myBookmarks$urlDF
})
if (!is.null(tmpUrlDF)) {
saveRDS(tmpUrlDF, "bookmarks.rds")
}
})

setBookmarkExclude(
c(
"bookmarkBtn",
"data_table_rows_all",
"data_table_rows_current",
"data_table_rows_selected",
"data_table_rows_search",
"data_table_rows_state",
"data_table_rows_last_clicked",
"bar",
"navbar",
"Scenario",
"description",
"urlTable_cell_clicked",
"urlTable_rows_all",
"urlTable_rows_current",
"urlTable_rows_selected",
"urlTable_search",
"urlTable_state",
"urlTable_row_last_clicked"
)
)

output$bookmarkplot <- renderPlot({
hist(faithful$eruptions[seq_len(input$n)], breaks = 40)
})

onBookmarked(
fun = function(url) {
if (!url %in% myBookmarks$urlDF$URL) {
if (is.null(myBookmarks$urlDF)) {
myBookmarks$urlDF <-
unique(
data.table(
Description = input$description,
URL = paste0("<a href='", url, "'>", url, "</a>"),
Timestamp = Sys.time(),
Session = session$token
),
by = "URL"
)
} else {
myBookmarks$urlDF <-
unique(rbindlist(list(
myBookmarks$urlDF,
data.table(
Description = input$description,
URL = paste0("<a href='", url, "'>", url, "</a>"),
Timestamp = Sys.time(),
Session = session$token
)
)), by = "URL")
}
}
}
)

output$urlTable = DT::renderDataTable({
req(myBookmarks$urlDF)
myBookmarks$urlDF
}, escape = FALSE)

enableBookmarking(store = "server")
}
shinyApp(ui = ui, server = server)

See ?enableBookmarking or my earlier answer.

R shiny bookmark bucket lists

The minimal example above is missing a few bits. Below is a minimal example showing bookmarking used to persistently store selections.

library(shiny)

ui <- function(request) {

fluidPage(
textInput("txt", "Text"),
checkboxInput("chk", "Checkbox"),
bookmarkButton()
)

}

server <- function(input, output, session) {

}

enableBookmarking("url")
shinyApp(ui, server)

Bucket lists don't appear to be natively supported by bookmarks. You can manually store and retrieve data from bookmarks using onBookmark and onRestore and then renderUI to update your bucket list.


Edit: Additional example showing the use of onBookmark and onRestore and then renderUI to get bucket lists working with bookmarks.

library(shiny)
library(sortable)

ui <- function(request) {

fluidPage(

uiOutput("bucket"),
bookmarkButton()

)

}

server <- function(input, output, session) {

output$bucket <- renderUI({

bucket_list(
header = "This is a bucket list. You can drag items between the lists.",
add_rank_list(
text = "Drag from here",
labels = c("a", "bb", "ccc"),
input_id = 'list1'
),
add_rank_list(
text = "to here",
labels = NULL,
input_id = 'list2'
)
)

})

#When creating a bookmark, manually store the selections from our bucket lists
onBookmark(function(state) {

state$values$list1 <- input$list1
state$values$list2 <- input$list2
})

#When restoring a bookmark, manually retrieve the selections from our bucket lists and re-render the bucket list input
onRestore(function(state) {

output$bucket <- renderUI({

bucket_list(
header = "This is a bucket list. You can drag items between the lists.",
add_rank_list(
text = "Drag from here",
labels = state$values$list1,
input_id = 'list1'
),
add_rank_list(
text = "to here",
labels = state$values$list2,
input_id = 'list2'
)
)

})

})

}

enableBookmarking("url")
shinyApp(ui, server)

R shiny bookmark button

As said by @Tschösi these values are hard coded in the function, which leaves you with 2 options:

  1. You create an own copy of the function, where you replace the hard coded versions.
  2. You apply some Javascript to change the text dynamically.

Personally, I'd favor option 2.

Here's a working example of how to do that:

library(shiny)
js <- HTML(paste("$(function() {",
"$('body').on('shown.bs.modal', function() {",
"$('.modal-dialog .modal-header > .modal-title').text('Speicher Lesezeichen')",
"$('.modal-dialog .modal-body > span:first').text('Dieser Link speichert den aktuellen Status der Applikation.')",
"$('#shiny-bookmark-copy-text').text('Drücken Sie Ctrl-C um den Link zu kopieren.')",
"$('.modal-dialog .modal-footer > button').text('Schließen')",
"})",
"})",
sep = "\n"))
ui <- function(request) {
fluidPage(
tags$head(tags$script(js, type = "text/javascript")),
tabsetPanel(id = "tabs",
tabPanel("One",
checkboxInput("chk1", "Checkbox 1"),
bookmarkButton(id = "bookmark1")
),
tabPanel("Two",
checkboxInput("chk2", "Checkbox 2"),
bookmarkButton(id = "bookmark2")
)
)
)
}
server <- function(input, output, session) {
# Need to exclude the buttons from themselves being bookmarked
setBookmarkExclude(c("bookmark1", "bookmark2"))

# Trigger bookmarking with either button
observeEvent(input$bookmark1, {
session$doBookmark()
})
observeEvent(input$bookmark2, {
session$doBookmark()
})
}
enableBookmarking(store = "url")
shinyApp(ui, server)

Modal with changed text



Related Topics



Leave a reply



Submit