Dynamic App Configuration

2024-05-27

Update app configuration and theme at runtime

Sometimes, you want to update the configuration of your app at runtime. Think about changing the global configuration for one or multiple components or updating the theme of your app.


In {shinyMobile}, there are two functions that can update (parts of) your app:

Updating theme, dark mode and color

In essence, updateF7App() is a pretty simple function: it takes a list of options and passes it to the running app instance.

updateF7App(
  options = list(
    theme = "md",
    dark = TRUE,
    color = "#007aff"
    # any other options
    # ...
  )
)

The following example shows how to update the theme, dark mode, and color theme of the app:

library(shiny)
library(shinyMobile)

colors <- c(
  lightblue = "#5ac8fa",
  pink = "#ff2d55",
  teal = "#009688",
  yellow = "#ffcc00"
)

shinyApp(
  ui = f7Page(
    title = "Update App",
    options = (
      list(
        color = "#5ac8fa"
      )
    ),
    f7SingleLayout(
      navbar = f7Navbar(title = "Update App"),
      f7BlockTitle("Update theme"),
      f7Segment(
        f7Button(
          inputId = "theme_ios",
          "iOS theme"
        ),
        f7Button(
          inputId = "theme_md",
          "MD theme"
        )
      ),
      f7BlockTitle("Set dark mode"),
      f7Segment(
        f7Button(
          inputId = "enable_darkmode",
          "Enable darkmode"
        ),
        f7Button(
          inputId = "disable_darkmode",
          "Disable darkmode"
        )
      ),
      f7BlockTitle("Change color theme"),
      f7Segment(
        tagList(
          lapply(names(colors),
                 function(color) {
                   f7Button(
                     inputId = paste0("color_", color),
                     label = color,
                     color = color,
                   )
                 }
          )
        )
      )
    )
  ),
  server = function(input, output, session) {

    observeEvent(input$theme_ios, {
      updateF7App(
        options = list(
          theme = "ios"
        )
      )
    })

    observeEvent(input$theme_md, {
      updateF7App(
        options = list(
          theme = "md"
        )
      )
    })

    observeEvent(input$enable_darkmode, {
      updateF7App(
        options = list(
          dark = TRUE
        )
      )
    })

    observeEvent(input$disable_darkmode, {
      updateF7App(
        options = list(
          dark = FALSE
        )
      )
    })

    lapply(names(colors), function(color) {
      observeEvent(input[[paste0("color_", color)]], {
        updateF7App(
          options = list(
            color = colors[color]
          )
        )
      })
    })

  }
)

Note that the color can both be a color of getF7Colors() (like “primary”, “pink” or “teal”) or a hexadecimal color representing those colors. You can call updateF7App multiple times, and the app will update the given configuration settings accordingly.

Updating component configuration

Besides updating the theme, dark mode, and color, you can also update the configuration of a specific type of component. For example, you can update the configuration of all the f7Dialog() components in an app:

updateF7App(
  options = list(
    dialog = list(
      buttonOk = "Yeaaaah!",
      buttonCancel = "Ouuups!"
    )
  )
)

Above code will update the text shown in the dialog buttons. In the below app, there are three f7Dialog()s that will be updated when the “Update config” button is clicked:

library(shiny)
library(shinyMobile)

shinyApp(
  ui = f7Page(
    title = "Update App",
    f7SingleLayout(
      navbar = f7Navbar(title = "Update App"),
      f7BlockTitle("Update f7Dialog configuration"),
      f7Segment(
        f7Button(
          inputId = "goButton",
          "Show f7Dialog 1"
        ),
        f7Button(
          inputId = "goButton2",
          "Show f7Dialog 2"
        ),
        f7Button(
          inputId = "update",
          "Update config"
        )
      ),
    )
  ),
  server = function(input, output, session) {
    observeEvent(input$goButton, {
      f7Dialog(
        id = "dialog1",
        title = "Dialog title",
        text = "This is a confirm dialog",
        type = "confirm"
      )
    })
    
    observeEvent(input$goButton2, {
      f7Dialog(
        title = "Another dialog title",
        text = "This is an alert"
      )
    })

    observeEvent(input$update, {
      updateF7App(
        options = list(
          dialog = list(
            buttonOk = "Yeaaaah!",
            buttonCancel = "Ouuups!"
          )
        )
      )
      
      f7Dialog(
        id = "dialog2",
        title = "Warning",
        type = "confirm",
        text = "Look at me, I have a new buttons!"
      )
            
    })
    
  }
)

There are many more components that can be updated in this way. For example:


Basically, whenever a component in the Framework7 documentation contains a section on “global app parameters”, you can update these parameters using updateF7App.

Update individual components at runtime

If you’re not looking to update the entire app or a particular group of components, but rather a specific component, you can use updateF7Entity. This function allows you to update a subset of Framework7 instances from the server:

updateF7Entity(
  id = "idtoupdate",
    options = list(
      # your configuration here
    )
)

All components that can be updated in this way are:


There are still specific update functions for some of the above components (like updateF7ActionSheet() and updateF7Gauge()), but the aim of updateF7Entity() is to provide a more general way to update them without calling their specific update function.


Below are some examples of how to use updateF7Entity().

Action Sheet

You could update (or reset) the content of an f7ActionSheet() like so:

library(shiny)
library(shinyMobile)

shinyApp(
  ui = f7Page(
    title = "Update Entity",
    f7SingleLayout(
      navbar = f7Navbar(title = "Update action sheet instance"),
      f7Segment(
        f7Button(
          inputId = "goButton",
          label = "Go!"
        ),
        f7Button(
          inputId = "update",
          label = "Update config"
        ),
        f7Button(
          inputId = "reset",
          label = "Reset"
        )
      )
    )
  ),
  server = function(input, output, session) {
    observeEvent(input$goButton, {
      f7ActionSheet(
        grid = TRUE,
        id = "action1",
        buttons = list(
          list(
            text = "Notification",
            icon = f7Icon("info"),
            color = NULL
          ),
          list(
            text = "Dialog",
            icon = f7Icon("lightbulb_fill"),
            color = NULL
          )
        )
      )
    })

    observeEvent(input$update, {
      updateF7Entity(
        id = "action1",
        options = list(
          buttons = list(
            list(
              text = "Notification",
              icon = f7Icon("info"),
              color = NULL
            )
          )
        )
      )
    })

    observeEvent(input$reset, {
      updateF7Entity(
        id = "action1",
        options = list(
          buttons = list(
            list(
              text = "Notification",
              icon = f7Icon("info"),
              color = NULL
            ),
            list(
              text = "Dialog",
              icon = f7Icon("lightbulb_fill"),
              color = NULL
            )
          )
        )
      )
    })
  }
)

In the above example, we update the content of buttons in the f7ActionSheet() with the id action1. This way, you can update the component at runtime.


A similar result can be achieved with updateF7ActionSheet().

Gauge

Changing the value of a gauge is a common task and perfectly possible with updateF7Entity():

library(shiny)
library(shinyMobile)

shinyApp(
  ui = f7Page(
    title = "Gauges",
    f7SingleLayout(
      navbar = f7Navbar(title = "f7Gauge"),
      f7Segment(
        f7Button(
          inputId = "update",
          label = "Update Gauge"
          ),
        f7Button(
          inputId = "reset",
          label = "Reset"
          )
      ),
      f7Block(
        f7Gauge(
          id = "mygauge",
          type = "semicircle",
          value = 50,
          borderColor = "#2196f3",
          borderWidth = 10,
          valueFontSize = 41,
          valueTextColor = "#2196f3",
          labelText = "amount of something"
        )
      )
    )
  ),
  server = function(input, output, session) {
    observeEvent(input$update, {
      new_val <- 75
      updateF7Entity(
        id = "mygauge",
        options = list(
          # Must be between 0 and 1
          value = new_val / 100, 
          valueText = paste0(new_val, "%"),
          labelText = "New label!"
        )
      )
    })
    
    observeEvent(input$reset, {
      updateF7Entity(
        id = "mygauge",
        options = list(
          value = 50 / 100,
          valueText = "50%",
          labelText = "amount of something"
        )
      )
    })
  }
)

A similar result can be achieved with the updateF7Gauge().

List Index

The f7ListIndex() component has limited configuration options. Most of the default options make the most sense for this particular component. But, you could update the label (bubble with selected index when you swipe over list index) of a f7ListIndex() component like so:

library(shiny)
library(shinyMobile)

shinyApp(
  ui = f7Page(
    title = "My app",
    f7SingleLayout(
      navbar = f7Navbar(title = "f7List"),
      f7Segment(
        f7Button(
          inputId = "update",
          label = "Update"
        ),
        f7Button(
          inputId = "reset",
          label = "Reset"
        )
      ),
      f7List(
            id = "mycontacts",
            mode = "contacts",
            lapply(1:3, function(i) {
              f7ListGroup(
                title = LETTERS[i],
                lapply(1:10, function(j) f7ListItem(letters[j]))
              )
            })
          )
    )
  ),
  server = function(input, output, session) {
    f7ListIndex(id = "contacts", 
                target = "#mycontacts", 
                label = TRUE)
    
    observeEvent(input$update, {
      updateF7Entity(
        "contacts",
        options = list(
          label = FALSE
        )
      )
    })
    
    observeEvent(input$reset, {
      updateF7Entity(
        "contacts",
        options = list(
          label = TRUE
        )
      )
    })
    
  }
)

Photo Browser

Using updateF7Entity(), you can update the photos that are displayed in the photo browser and any other configuration:

library(shiny)
library(shinyMobile)

shinyApp(
  ui = f7Page(
    title = "f7PhotoBrowser",
    f7SingleLayout(
      navbar = f7Navbar(title = "f7PhotoBrowser"),
      f7Segment(
        f7Button(
          inputId = "togglePhoto",
          label = "Open photo"
        ),
        f7Button(
          inputId = "update",
          label = "Update"
        )
      )
    )
  ),
  server = function(input, output, session) {
    observeEvent(input$togglePhoto, {
      f7PhotoBrowser(
        id = "photobrowser1",
        theme = "dark",
        type = "page",
        photos = list(
          list(url = "https://cdn.framework7.io/placeholder/sports-1024x1024-1.jpg"),
          list(url = "https://cdn.framework7.io/placeholder/sports-1024x1024-2.jpg"),
          list(url = "https://cdn.framework7.io/placeholder/sports-1024x1024-3.jpg",
               caption = "Me cycling")
        ),
        thumbs = c(
          "https://cdn.framework7.io/placeholder/sports-1024x1024-1.jpg",
          "https://cdn.framework7.io/placeholder/sports-1024x1024-2.jpg",
          "https://cdn.framework7.io/placeholder/sports-1024x1024-3.jpg"
        )
      )
    })

    observeEvent(input$update, {
      updateF7Entity(
        "photobrowser1",
        options = list(
          type = "popup",
          popupPush = TRUE,
          toolbar = FALSE,
          photos = list(
          list(url = "https://cdn.framework7.io/placeholder/sports-1024x1024-1.jpg")
          ),
          thumbs = list("https://cdn.framework7.io/placeholder/sports-1024x1024-1.jpg")
        )
      )
    })
  }
)

Swiper

There are many options to customize the f7Swiper() component. Note that on desktop you need to click and hold to swipe:

library(shiny)
library(shinyMobile)

shinyApp(
  ui = f7Page(
    title = "Swiper",
    f7SingleLayout(
      navbar = f7Navbar(title = "f7Swiper"),
      f7Segment(
        f7Button(
          inputId = "update",
          label = "Update"
        ),
        f7Button(
          inputId = "reset",
          label = "Reset"
        )
      ),
      f7Swiper(
        id = "swiper",
        options = list(
          navigation = list(
            enabled = FALSE
          ),
          pagination = list(
            enabled = TRUE,
            el = ".swiper-pagination", 
            clickable = TRUE
          )
        ),
        lapply(1:20, \(c) {
          f7Slide(
            f7Card(
              title = sprintf("Slide %s", c)
            )
          )
        })
      )
    )
  ),
  server = function(input, output) {
    observeEvent(input$update, {
      updateF7Entity(
        "swiper",
        options = list(
          speed = 100,
          slidesPerView = 2,
          spaceBetween = 10,
          autoplay = TRUE,
          scrollbar = list(
            enabled = FALSE
          ),
          pagination = list(
            type = "progressbar",
            enabled = TRUE
          ),
          grid = list(
            fill = "columns",
            rows = 4
          )
        )
      )
    })
    
    observeEvent(input$reset, {
      updateF7Entity(
        "swiper",
        options = list(
          speed = 400,
          slidesPerView = "auto",
          spaceBetween = 50,
          autoplay = FALSE,
          scrollbar = list(
            enabled = TRUE
          ),
          pagination = list(
            type = "bullets",
            enabled = TRUE
          ),
          grid = list(
            fill = "column",
            rows = 1
          )
        )
      )
    })
  }
)