How to externally control the inputs of a shiny app?

I'm looking for software architectural help, that I didn't see discussed previously.

Imagine we want to split the inputs and outputs of a Shiny App onto two different apps, yet not lose the fast responsiveness of Shiny. How would you implement it?
...

If you think, why would anybody in the world want to do this? Imagine a Shiny Dashboard on a TV in your company, where you can't directly change any inputs and you use your smartphone to open an "input app" where you can change parameters as you stand in front of the dashboard.

1 Like

I think you would need two separate app's and a common backend (API) or database. In a simple form, the "control" app will write parameters to a database and the "display" app will read those parameters and react to them.

You could even go further and use a physical interface to set parameters (like a remote control) and send those parameters to the "display" app using something like the MQTT protocol.

Hi Ben :wave:

Interesting question. I think there would be a way to do this within a single app, which would require 2 important features:

  1. Have conditional UI, depending on device type (this is assuming your "remote control" will always be a phone or tablet). This way, you can just show the input controls in the "remote" version, and the outputs in the "tv" version (i.e. a monitor connected to a computer running the app in a desktop browser).
  2. Define your reactivity at the process (global) level and not at the session level (i.e., inside the server). This way, any changes done in the remote control will be refected on the tv (even if they are running on separate sessions).

Depending on how/where you plan to deploy this app, it will probably require to tweak the performance settings a bit to avoid spawning new processes (which would make feature 2. not work) on new connections.

So, at least in theory, I think this could be done.

Hope this helps :slight_smile:
Cheers!


This post was published by an Appsilon team member. Our company can help you get the most out of RShiny and Posit/RStudio products.

Check our open positions here.

Appsilon: Building impactful RShiny Dashboards and providing R coding services.
Appsilon_GIFsmall_whitebg

2 Likes

This is interesting, I thought about conditionally rendering the UI but I didn't knew you can define reactivity outside of the server function which to my understanding of the scoping rules was "per session".
It would be very interesting if you could provide a simple example to ilustrate this approach.

Yes, database and API were also our first ideas, but we thought Shiny devs and the R community have a more elegant way to solve this. I love your point to MQTT - great idea to use, could open the door to completely different ecosystems like IFTTT etc.

Yet with all dose third-party systems, it adds unpleasant delays - you will accept 500ms if you open your garage, but it's annoying when you try a graphical user interface. for a simple input, like a switch or single-input kind it might work well.

Hi Andres :wave:

Sure. Here I use {R6} to manage a reactive value at the process level.
You can run the app, and then open a new session (i.e., copy+paste the URL in a different browser tab) and check that they are sync'd.

library(shiny)
library(R6)

# Create a process-level (i.e. in global scope) reactive-value manager.
ValueManager <- R6::R6Class(
  classname = "ValueManager",
  public = list(
    initialize = function() {
      private$reactive_inner_counter <- reactiveVal(1)
    },
    set_value = function(value) {
      private$reactive_inner_counter(value)
    },
    get_value = function() {
      private$reactive_inner_counter()
    }
  ),
  private = list(
    reactive_inner_counter = NULL
  )
)

# Instantiate the manager outside the server function..
value_manager <- ValueManager$new()

ui <- fluidPage(
  sliderInput("slider_value", label = "# of rows to show",min = 1, max = 50, value = 10),
  tableOutput("table")
)

server <- function(input, output, session) {
  observeEvent(input$slider_value, {
    value_manager$set_value(input$slider_value)
  })

  output$table <- renderTable({
    current_slider_value <- value_manager$get_value()
    iris[1:current_slider_value, ]
  })
}

shinyApp(ui, server)

Cheers!


This post was published by an Appsilon team member. Our company can help you get the most out of RShiny and Posit/RStudio products.

Check our open positions here.

Appsilon: Building impactful RShiny Dashboards and providing R coding services.
Appsilon_GIFsmall_whitebg

2 Likes

I get your point but I think it might be negligible compared to the time it takes for the R output (plots, tables, etc) to be rendered anyways.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.