RafaelRS
RafaelRS

Reputation: 23

ifelse inside observe in shiny - not waiting for the trigger

At some part of my code I have the following observe object. It works fine until I add an if statment inside it. The statment executes right away when the script runs and does not wait for the action associated to observe to be triggered. What do I have to do in order to "hold the horses" of this if inside observe?

observe({
    print("UPDATING COORDS")
    print(input$map_marker_dragend)
    id <- input$map_marker_dragend$id
    lat_nova <- input$map_marker_dragend$lat
    lon_nova <- input$map_marker_dragend$lng
    print(lat_nova)
    print(lon_nova)
    print(WP$df$latitude)
    df2 <- ifelse(is.null(df2), WP$df, df2) # this is the line causing problem
    df2 <- if (is.null(df2)) return(WP$df) else return(df2) # this is a second try to make it work
    df2$latitude[which(WP$df$id == id)] <- lat_nova
    df2$longitude[which(WP$df$id == id)] <- lon_nova
    print(df2$latitude)
    print(df2, width = Inf)

    # declaring to later use
    WP$df2 = df2
  })

Basically what I want is that shiny checks if df2 exists... if not than df2 should be WP$df... if yes than df2 = df2 and proceed with calculations.

Thanks!

Upvotes: 0

Views: 87

Answers (1)

r2evans
r2evans

Reputation: 160687

ifelse is vectorized, and the length of the return value is dependent on the length of the conditional (first argument). I'm inferring that your df2 and WP$df2 are data.frames, which means that while the length if is.null(df2) is length 1, the "length" of WP$df is not, it is actually the same as ncol(WP$df). This can be seen here:

length(mtcars)
# [1] 11
ncol(mtcars)
# [1] 11
nrow(mtcars)
# [1] 32

ifelse(TRUE, mtcars, iris)
# [[1]]
#  [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2 10.4 10.4 14.7 32.4
# [19] 30.4 33.9 21.5 15.5 15.2 13.3 19.2 27.3 26.0 30.4 15.8 19.7 15.0 21.4

That is the first column (as a list) of mtcars. (It might be helpful to know that a data.frame is really just a list where each element is the same length. In traditional ways, each element is a vector, but more complicated objects ("list-columns") can be had as well.)

It is often assumed that the lengths of the test=, yes=, and no= arguments to ifelse are either all of the same length or of length 1 (which allows recycling). But this is neither tested nor enforced:

ifelse(TRUE, 1:2, 11:13)
# [1] 1
ifelse(c(FALSE, TRUE), 1, 11:13)
# [1] 11  1ifelse(c(FALSE, TRUE, FALSE), 1, 11:12)
# [1] 11  1 11

From this, it can be inferred that the objects are recycled as much as required to fill out to the length of test=.

So in your case, your ifelse(is.null(df), WP$df, df2) is returning just the first column of one of the two.

Something else about your code: don't use return.

I suggest your code should be

observe({
    print("UPDATING COORDS")
    print(input$map_marker_dragend)
    id <- input$map_marker_dragend$id
    lat_nova <- input$map_marker_dragend$lat
    lon_nova <- input$map_marker_dragend$lng
    print(lat_nova)
    print(lon_nova)
    print(WP$df$latitude)
    if (is.null(df2)) df2 <- WP$df
    df2$latitude[which(WP$df$id == id)] <- lat_nova
    df2$longitude[which(WP$df$id == id)] <- lon_nova
    print(df2$latitude)
    print(df2, width = Inf)

    # declaring to later use
    WP$df2 = df2
  })

(I'm assuming WP is from reactiveValues, otherwise the assignment on the last line will not be saved.)

Upvotes: 1

Related Questions