Link Search Menu Expand Document

Bookmark Handling

An essential feature of the MDI apps framework is sophisticated bookmarking that is more versatile and user-friendly than the approaches used by R Shiny, where an MDI bookmark is a JSON-formatted text file with extension ‘.mdi’ that carries comprehensive information on the state of an app to allow it to be seamlessly reloaded.

Bookmarking is implemented at the level of the appStep, i.e., each appStep module is responsible for declaring and acting on its bookmark contents. This is accomplished by including save and load instructions at the end of an appStep module.

Specifying appStep values to save in a bookmark

Three optional elements of an appStep return list are always saved to bookmarks, in addition to being available for use in the app:

  • input = the current state of inputs on the appStep page
  • settings = the current state of inputs under the appStep-level settings icon
  • outcomes = a reactive, or a list of reactives, that return(s) values derived from user actions
# <appStep>/<appStep>_server.R
appStepServer <- function(id, options, bookmark, locks) {
    moduleServer(id, function(input, output, session) {
    # ...
    list( # the module's return value
        input = input,
        settings = settings$all_,
        outcomes = reactive({ list(
            <outcomeName> = ...
        ) })
        # outcomes = list(  # alternative use
        #     <outcomeName> = reactive(...)
        # )
    )
})}

Importantly, ONLY input, settings, and outcomes values are stored in bookmarks. All other named objects in the module return value list are considered to be for app internal use only. If you need to save something that is not an input or a setting, be sure to declare it as an outcome, where ‘outcomes’ are data states derived from user actions.

Restoring appStep state from an incoming bookmark

When a user loads a bookmark from the framework launch page, that information is passed to all appStep modules via the bookmark reactive. The following code illustrates how to handle the incoming bookmark’s contents.

# <appStep>/<appStep>_server.R
appStepServer <- function(id, options, bookmark, locks) {
    moduleServer(id, function(input, output, session) {
    # ...
    observe({
        bm <- getModuleBookmark(id, module, bookmark, locks)
        req(bm)
        settings$replace(bm$settings) # if using step-level settings
        if(!is.null(bm$outcomes)) {
            # reload outcomes as needed
        }
    })
    # module return value
})}

By convention and for clarity, always place the bookmark observer in the penultimate position of the appStep server function, just above the module return value that declares the contents of the bookmark.

For backwards compatibility, please ensure that your appStep can load older bookmarks that might lack features of newer bookmarks by providing overrides when bookmark elements are absent.

Auto-saved bookmarks and page reloading

In addition to user-saved bookmarks, the apps framework automatically creates a bookmark every time a session ends, i.e., when the user closes or reloads the web browser page, which supports two additional features:

  • quick launch to the most recent state using the “auto saved” entry on the launch page
  • page reloading by clicking the top-left page label (“MDI” by default)