Updating an input and using the new value immediately

2,593 views
Skip to first unread message

Dean Attali

unread,
May 20, 2015, 8:06:02 PM5/20/15
to shiny-...@googlegroups.com
I noticed that if I call "updateXyzInput" in a reactive context and then go on to use that value in some UI rendering, then the UI does indeed show the latest updated value.  But if I try to access the value in R code, I get the previous pre-updated value.

Example:

getTime <- function() as.integer(Sys.time())
runApp(list(
  ui = fluidPage(
    textInput("time", "Time", getTime()),
    actionButton("update", "Update")
  ),
  server = function(input, output, session) {
    observeEvent(input$update, {
      updateTextInput(session, "time", value = getTime())
      cat(input$time, "\n")
    })
  }
))

Whenever you click on "Update", the text input will be updated with the current time, but the console will print the previous value the input had.
If I would change the "cat" statement to a "renderText" then the text printed in the app would indeed match the input value, but my problem is that I need to use the new value in code that has nothing to do with the UI.

I'm sure there's just a small bit of technical knowledge about shiny's inner workings that would tell me how to achieve what I want, does anyone know?

Sigbert Klinke

unread,
May 21, 2015, 7:02:34 AM5/21/15
to shiny-...@googlegroups.com
Hi,

if the server function is called then, I guess,  input is a deep copy of the current state of the input elements. Otherwise Shiny could not ensure that all expressions in the server function will have the same input values which may lead to some output with the old values and some output with the new values. But even when Shiny is not making a deep copy then R is making a deep copy when you are accessing input.  Maybe you can track down in updateTextInput how sendInputMessage is working.

Sigbert

Norman Packard

unread,
May 22, 2015, 9:31:54 PM5/22/15
to shiny-...@googlegroups.com
You get your desired behavior (I think) if you separate reaction to the update button from reaction to input$time:
 
runApp(list(
  ui = fluidPage(
    textInput("time", "Time", getTime()),
    actionButton("update", "Update")
  ),
  server = function(input, output, session) {
    observeEvent(input$update, {
      updateTextInput(session, "time", value = getTime())
    })
    observe({

Dean Attali

unread,
May 22, 2015, 10:24:15 PM5/22/15
to shiny-...@googlegroups.com
Norman - that would indeed work, but I was hoping to be able to use the updated value in the same reactive context.  

Wayne Haythorn

unread,
May 26, 2015, 10:30:50 PM5/26/15
to shiny-...@googlegroups.com
Perhaps I don't understand your question, but this does not seem complicated.  updateTextInput returns when it has sent a message to the client, telling it to update the text box. You can see this in the shiny code, here: https://github.com/rstudio/shiny/blob/89bc7efbca09a068dd3757dd798248eaac06e80c/R/update-input.R
The textInput will not change until that message has been received by the client.  You will then get a message back from the client, telling you that input$time has changed, but you don't have an observer so you won't know when that happens. If time on the server is what you want, you can write this:
observeEvent(input$update, {
     temp <- getTime()
updateTextInput(session, "time", value = temp)
     cat(temp, "\n")
    })

If you want the time on the client, I think you have to write a javascript function as part of ui.R.

Dean Attali

unread,
May 26, 2015, 10:45:28 PM5/26/15
to shiny-...@googlegroups.com
Wayne, what you said is right.  But it seems that what I actually want to do is not possible. As you said - after calling the update function, the value in `input` will not change in the current context because the input has to first change on the client side and the message has to come back to the server side.

Your solution does work in this particular case; instead of using `input$time` I can use the value that I know is correct. But I wanted a more general solution that will work with multiple variables being updated. Imagine something like this

    observeEvent(input$update, {
     updateTextInput(session, "time", value = getTime())

     updateTextInput
(session, "test1", value = "test1")
     updateTextInput
(session, "test2", value = "test2")
     helperFunction
(input)
    })

I want to be able to just pass `input` to the helper function with the updated values, without having to set them manually or pass the variables separately.  I'm starting to realize that might not be possible.

Sigbert Klinke

unread,
May 28, 2015, 5:29:42 AM5/28/15
to shiny-...@googlegroups.com
Hi,

you could modify the updateTextInput function, e.g.

inp <- list(a=1)

helperFunction
<- function(input) { str(input) }

server
<- function (session, input) {
    helperFunction
(input)
    myUpdateTextInput
(session, "time", value = 0)
    myUpdateTextInput
(session, "test1", value = "test1")
    myUpdateTextInput
(session, "test2", value = "test2")
    helperFunction
(input)
}

myUpdateTextInput
<- function (session, inputId, label = NULL, value = NULL)
{ ##maybe another environment instead of "parent.frame()" is necessary
  input
<- get("input", envir = parent.frame())
  input
[[inputId]] <- value  
  assign
("input", input, envir = parent.frame())
 
##next line commented such that the program runs without shiny library
 
#updateTextInput(session, inputId, label, value)
}

server
(list(),inp)



Reply all
Reply to author
Forward
0 new messages