Reputation: 1232
I think I have run into some strange scoping issue that I have fought with for a week now without understanding what is going on. I have also been unable to really make a small example that have the same problem but I hope the symptoms ring some bells. The real code is also available but the app is pretty complex.
Let me explain the players in the code.
A number of inputs in a bsModal
, mainly numericInput
.
An observeEvent
, lets call it "the reader", fires when a file is read that contains cached results. It updates a reactiveValues
object that contains the equivalent of all the inputs in a special S4 object.
Then we have an observe
, lets call it "the object creator" that takes all the inputs and updates the reactiveValues` object if any inputs are changed.
an observeEvent
, lets call it "the input updater", that fires when the reactiveValues
reactive is invalidated and should update all the inputs. This is done to allow other processes to change the inputs by changing the reactiveValues
reactive (for example "the object creator"). The first functionality I need is simply that it updates the inputs when the cached results are read by the "the object creator".
So it should go:
"the reader" reads a file --> "the input updater" sees a new reactiveValues
reactive and updates the inputs (--> the "the object creator" sees new inputs and re-writes the reactiveValues
reactive but they should be what the "the reader" already set).
The issue I have is in the "the input updater". I cannot get it to update the input based on the reactiveValues
.
The code looks like this:
observeEvent(settings$processing_local, {
cat("\n\n\n")
print("Modifying inputs")
print(paste0("before ppm input is: ", input$local_ppm))
set <- ppm(settings$processing_local) # THIS DOES NOT WORK
print(paste0("setting: ", set)) # SHOWS CORRECT VALUE
# set <- 1000 # THIS WORKS!
updateNumericInput(session,"local_ppm",value = set)
print(paste0("after ppm input is: ", input$local_ppm))
cat("\n\n\n")
}, priority = 2)
When set
is based on the reactiveValues
settings$processing_local
then the update doesn't happen. The crazy thing is that the output of print
does show the right value AND if I hardcode a value to set
then it also works.
The full code for 1, 2, 3 and 4.
EDIT 1: Version of the relevant processes based on the example of @cuttlefish44 This is closer to my action app but unfortunately does not have the problem I am experiencing in the full app.
ui <- fluidPage(
numericInput("inNumber", "Input number", 0),
actionButton("but", "Update")
)
server <- function(input, output, session) {
settings <- reactiveValues(aaa = NULL)
# proxy for reading cached file
observeEvent(input$but, {
settings$aaa <- 30
})
observe({
settings$aaa <- input$inNumber
}, priority = 1)
observeEvent(settings$aaa, {
set <- settings$aaa
print(c(set, input$inNumber))
updateNumericInput(session, "inNumber", value = set)
print(c(set, input$inNumber))
}, priority = 2)
}
shinyApp(ui, server)
EDIT 2: In lieu of a working example of the issue I have dockerized my app so it should be possible to see the issue albeit annoying to do. Dockerized app here.
Can be build with docker build --tag mscurate .
and run with docker run --publish 8000:3838 mscurate
.
After starting the app the issue can be seen by:
The logging shows the sequence of events when loading the file:
-------Loading started-------
before the reactive is:
settings not present
after the reactive is:
500
-------Loading finished-------
-------Modifying inputs-------
before ppm input is: 100
setting: 500
after ppm input is: 100 <---- @cuttlefish44's answer explains why this is not updated
--------------
-------updating reactive objects-------
before ppm input is: 100 <---- this should have been updated to 500!
before the reactive object is: 500
after ppm input is: 100
after the reactive object is: 100
--------------
-------Modifying inputs-------
before ppm input is: 100
setting: 100
after ppm input is: 100
--------------
-------updating reactive objects-------
before ppm input is: 100
before the reactive object is: 100
after ppm input is: 100
after the reactive object is: 100
--------------
-------updating reactive objects-------
before ppm input is: 100
before the reactive object is: 100
after ppm input is: 100
after the reactive object is: 100
--------------
Upvotes: 0
Views: 1154
Reputation: 11878
I think the problem here is that the “object creator” has a reactive
dependency on the RVs, causing it to invalidate in the same cycle as the
“input updater” when the “reader” updates the RVs. Then the settings are overwritten by the old input
values before the update from the "input updater" takes place in the next cycle.
My interpretation of a play-by-play walkthrough would look something like this:
To fix this, remove the RV dependencies from the “object creator”, e.g. with
isolate()
. I couldn’t get req()
to work with isolate()
,
but in this case you could just drop that altogether.
Here’s a minimal example with the problem. Removing the req()
here fixes it:
library(shiny)
lgr <- list(debug = function(...) cat(sprintf(...), "\n"))
ui <- fluidPage(
sliderInput("file_number", "Number to \"read from file\"", 0, 10, 5),
actionButton("read", "Read"),
numericInput("number", "Input number to sync", 0)
)
server <- function(input, output, session) {
settings <- reactiveValues(number = NULL)
observeEvent(input$read, {
settings$number <- input$file_number
lgr$debug("Loaded settings from \"file\".")
}, label = "reader")
observe({
req(settings$number) # The dependency on settings
settings$number <- input$number
lgr$debug("Updated settings from input.")
}, priority = 1, label = "object-creator")
observeEvent(settings$number, {
updateNumericInput(session, "number", value = settings$number)
lgr$debug("Set input from settings: %d", settings$number)
}, priority = 2, label = "input-updater")
}
shinyApp(ui, server)
And the log produced after clicking “read”:
Loaded settings from "file".
Set input from settings: 5
Updated settings from input.
Set input from settings: 0
Updated settings from input.
Updated settings from input.
You can get a good look at the process with reactlog:
reactlog::reactlog_enable()
reactlogReset()
shinyApp(ui, server)
reactlogShow()
Upvotes: 2
Reputation: 6786
Your code works, but as far as I see input
is static in the event.
See below simple example.
ui <- fluidPage(
sliderInput("controller", "Controller", 0, 20, 10),
numericInput("inNumber", "Input number", 0),
)
server <- function(input, output, session) {
settings <- reactiveValues(aaa = NULL)
observe({
settings$aaa <- input$controller + 3
}, priority = 1)
observeEvent(settings$aaa, {
set <- settings$aaa
print(c(input$controller, set, input$inNumber))
updateNumericInput(session, "inNumber", value = set)
print(c(input$controller, set, input$inNumber))
}, priority = 2)
}
shinyApp(ui, server)
I change controller 10 (default) to 12.
# this is console output
[1] 10 13 0
[1] 10 13 0
[1] 12 15 13
[1] 12 15 13
and the UI screen shot shows inNumber
is updated to 15.
But console output shows input isn't updated immediately.
(maybe the updated value is in somewhere of session
but I don't know where)
Upvotes: 1