I have a shiny-module which uses reactive Values to store its internal state. In the example below this is only used to output the number the input changed, but my real use-cases are more complicated.
I now want to create a function which can be used to set one of these modules to the state of the other, including the internal state - or more generally: I want to create a function updateModule, which can also update the internalt state.
So my question is: How can I access and change a modules internal reactiveValues from the outside?
Another, related question for my special purpose is: How can I prevent the internal reactiveValue from updating when updating the Input - or how can I reset it(leading back to the main question?
For now, I am aware of two possible workarounds:
- Store internal state in hidden Input
- Use the call-by-reference-logic of data.table A,B (See below). There are other ways of implementing call-by-reference, but haven't used them yet.
However I would be interested to know whether there are more direct solutions, also because the internals I want to update are more complicated lists.
Example Code
#Problem: How to change reactiveValues from the outside?
library(shiny)
moduleUI <- function(id, label=id,min = 0,max = 100,value = 30){
ns <- NS(id)
fluidRow(
column(width=9,
sliderInput(ns("sl"), label=label, min=min, max=max, value=value)
),
column(width=2,
textOutput(ns("changesCount") )
)
)
}
synchModule<-function(session, targetModule, oldModule){
ns<-NS(targetModule)
updateSliderInput(session,ns("sl"),value=oldModule() )
##Accessing and changing internal Value of targetModule??
}
module<- function(input, output, session){
rv<-reactiveValues(changesCount=0)
observeEvent(input$sl,rv$changesCount<-rv$changesCount+1)
output$changesCount=renderText(rv$changesCount)
return(reactive({
ret <- input$sl
attr(ret,"changesCount")<-rv$changesCount
ret
}))
}
ui=fluidPage(
moduleUI("module1"),
moduleUI("module2"),
actionButton("synchButton", "Set Module 2 to state of Module 1."),
textOutput("module1state"),
textOutput("module2state")
)
server= function(input, output, session) {
module1<-callModule(module,"module1")
module2<-callModule(module,"module2")
observeEvent(input$synchButton, synchModule(session,"module2",module1)
)
output$module1state=renderPrint(module1() )
output$module2state=renderPrint(module2() )
}
shinyApp(ui, server)
Workaround 1: using hidden NumericInput
#Problem: How to change reactiveValues from the outside?
##Workaround using hidden input
library(shiny)
library(shinyjs)
moduleUI <- function(id, label=id,min = 0,max = 100,value = 30){
ns <- NS(id)
fluidRow(
column(width=9,
sliderInput(ns("sl"), label=label, min=min, max=max, value=value)
),
column(width=2,
textOutput(ns("changesCount") ),
hidden(numericInput(
ns("changesCountNumeric"), "If you can see this, you forgot useShinyjs()", 0)
)
)
)
}
synchModule<-function(session, targetModule, oldModule){
ns<-NS(targetModule)
updateSliderInput(session,ns("sl"),value=oldModule() )
updateNumericInput(session,ns("changesCountNumeric"),
value=attr(oldModule(),"changesCount")-1) #-1 to account for updating slider itself,
}
module<- function(input, output, session){
observeEvent(input$sl,
updateNumericInput(session,"changesCountNumeric",
value=input$changesCountNumeric+1)
)
output$changesCount=renderText(input$changesCountNumeric)
return(reactive({
ret <- input$sl
attr(ret,"changesCount")<-input$changesCountNumeric
ret
}))
}
ui=fluidPage(
useShinyjs(),
moduleUI("module1"),
moduleUI("module2"),
actionButton("synchButton", "Set Module 2 to state of Module 1."),
textOutput("module1state"),
textOutput("module2state")
)
server= function(input, output, session) {
module1<-callModule(module,"module1")
module2<-callModule(module,"module2")
observeEvent(input$synchButton, synchModule(session,"module2",module1)
)
output$module1state=renderPrint(module1() )
output$module2state=renderPrint(module2() )
}
shinyApp(ui, server)
P.s: I am not sure whether to put my workarounds as solutions or not.