1

I have designed a Shiny app with two action buttons, Save and Clear. When users click Save, the input values will be stored using local storage of the web browser. When users click Clear, the inputs and local storage would be cleaned.

These functionalities involves updating multiple inputs with update...Input. In this example, I have three inputs all with different widget types. I can specifically write them one by one, which works fine. However, I am wondering if there is a more efficient way to achieve this, such as using for loop or lapply. From this post (https://stackoverflow.com/a/41061114/7669809), it seems like I can use reactiveValuesToList to get all inputs. The real challenge to me is how to dynamically call different update...Input functions for the associated input widget types?

Please let me know if you have any suggestions.

### This script creates an example of the shinystore package

# Load packages
library(shiny)
library(shinyStore)

ui <- fluidPage(
  headerPanel("shinyStore Example"),
  sidebarLayout(
    sidebarPanel = sidebarPanel(
      initStore("store", "shinyStore-ex1"),
      textInput(inputId = "Text1", label = "Enter some texts")
    ),
    mainPanel = mainPanel(
      fluidRow(
        numericInput(inputId = "Number1", label = "Enter a number", value = NA),
        sliderInput(inputId = "Slider1", label = "Pick a number", min = 0, max = 100, value = 50),
        actionButton("save", "Save", icon("save")),
        actionButton("clear", "Clear", icon("stop"))
      )
    )
  )
)

server <- function(input, output, session) {
  observe({
    if (input$save <= 0){
      updateTextInput(session, inputId = "Text1", value = isolate(input$store)$Text1)
      updateNumericInput(session, inputId = "Number1", value = isolate(input$store)$Number1)
      updateSliderInput(session, inputId = "Slider1", value = isolate(input$store)$Slider1)
    }
    updateStore(session, name = "Text1", isolate(input$Text1))
    updateStore(session, name = "Number1", isolate(input$Number1))
    updateStore(session, name = "Slider1", isolate(input$Slider1))
  })
  
  observe({
    if (input$clear > 0){
      updateTextInput(session, inputId = "Text1", value = NA)
      updateNumericInput(session, inputId = "Number1", value = NA)
      updateSliderInput(session, inputId = "Slider1", value = 50)
      
      updateStore(session, name = "Text1", value = NA)
      updateStore(session, name = "Number1", value = NA)
      updateStore(session, name = "Slider1", value = 50)
    }
  })
}

shinyApp(ui, server)
www
  • 38,575
  • 12
  • 48
  • 84

1 Answers1

1

Unfortunately there is no generic updateInput function in shiny. It is still possible to build a wrapper that identifies a certain name to a certain input type, but that will also require to know which argument is allowed or no. For example, updateActionButton doesn't have value or choices as an argument so we'll need numerous if statements.

A possible workaround is to take advantage of renderUI and directly pass the stored values. The only downside is that some functions like SliderInput throw an error when some arguments are NULL, so an if statement is needed to appoint a default value for the first time the app runs. Alternatively a mock app can be executed once to only fill the first values.

Code:

library(shiny)
library(shinyStore)
library(tidyverse)

ui <- fluidPage(
    initStore("store", "shinyStore-ex1"),
    uiOutput('ui_all'))

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

    output$ui_all <- renderUI({
      tagList(
        headerPanel("shinyStore Example"),
        sidebarLayout(
            sidebarPanel = sidebarPanel(
                textInput(inputId = "Text1", label = "Enter some texts",value = input$store$Text1)
            ),
            mainPanel = mainPanel(
                fluidRow(
                    numericInput(inputId = "Number1", label = "Enter a number", value = input$store$Number1),
                    sliderInput(inputId = "Slider1", label = "Pick a number", min = 0, max = 100, value = if(is.null(input$store$Slider1)){50} else{input$store$Slider1}),
                    actionButton("save", "Save", icon("save")),
                    actionButton("clear", "Clear", icon("stop"))
                )
            )
        ))
    })
    
     
    input_nms <- map(c('Text', 'Number', 'Slider'), ~str_c(.x, 1:1)) %>%
                    reduce(c)
    
    #or if every type of input is repeteated n different times.
    # input_nms <- map2(c('Text', 'Number', 'Slider'), c(n1, n2, n3), ~str_c(.x, 1:.y)) %>%
    #     reduce(c)
    
        
    observeEvent(input$save, {
       input_nms %>% 
            walk(~updateStore(session = session, name = .x, value =  isolate(input[[.x]])))
            session$reload() #to force the UI to render again with the new values
    })
    
    observeEvent(input$clear, {
        input_nms %>% 
            walk2(c(NA, NA, 50), ~updateStore(session = session, name = .x, value = .y))
    })
}

shinyApp(ui, server)
jpdugo17
  • 6,816
  • 2
  • 11
  • 23