Observer class - details

246 views
Skip to first unread message

Alex W

unread,
Dec 31, 2015, 4:33:29 PM12/31/15
to Shiny - Web Framework for R
Is there a better resource for details behind the observer reference class besides `?shiny::observe`?

I'm getting the following error with some interaction between another package that I'm using and an observer classes:


server.R

dat <- reactiveValues(v1= NULL, v2= NULL)

dat$v1 <- observeEvent(...) # works
dat$v2 <- observeEvent(...) # doesn't work

Warning in if (class(object) == "geo.set") { :
  the condition has length > 1 and only the first element will be used
Warning in if (class(object) == "geo.set") { :
  the condition has length > 1 and only the first element will be used
Warning: Error in <Anonymous>: unable to find an inherited method for function ‘api.for’ for signature ‘"Observer"’
Stack trace (innermost first):
    72: <Anonymous>
    71: api.for
    70: paste
    69: paste0
    68: api.url.maker
    67: acs.fetch
    66: cbind
    65: acs.fetch
    64: dat_func
    63: observeEventHandler [C:\Github\Shiny_demo/server.R#79]
     1: shiny::runApp


> acs:::api.for
nonstandardGenericFunction for "api.for" defined from package "acs"

function (object) 
{
    standardGeneric("api.for")
}

Joe Cheng

unread,
Dec 31, 2015, 4:38:23 PM12/31/15
to Alex W, Shiny - Web Framework for R
What do you think the return value of observeEvent(...) is supposed to represent? There's no "value" that you can get out of it--that object only exists so you can suspend/resume the observer from executing, or destroy it altogether.

It's more likely you want to do something like this:

dat <- reactiveValues(v1 = NULL, v2 = NULL)

observeEvent(input$button1, {
  dat$v1 <- ...
})
observeEvent(input$button2, {
  dat$v2 <- ...
})

Or better yet, forget the reactiveValues entirely and do:

r1 <- eventReactive(input$button1, {...})
r2 <- eventReactive(input$button2, {...})

and use r1() and r2() everywhere you were going to read dat$v1 and dat$v2.

If this doesn't make any sense, feel free to show more of your code and we can get concrete.

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/195bc5b5-09c5-4872-b6c7-7008d1c06b8c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alex Whitworth

unread,
Dec 31, 2015, 4:58:20 PM12/31/15
to Joe Cheng, Shiny - Web Framework for R
Thanks for the quick response!

I have tried getting rid of reactiveValues and get the same result.

Moving to eventReactive may be the appropriate approach, I wasn't particularly able to tell from this documetation: http://shiny.rstudio.com/reference/shiny/latest/observeEvent.html 

I'm trying to rerun the test using eventReactive. My wi-fi appears to be running a pull to the api, but I'm not entirely sure. My test output is below. I should note that the call to the api takes a solid amount of time to pull, hence the eventReactive

server.r

output$test2 <- renderTable({
    if (is.null(dat$v2)) return(NULL)
    else dat$v2$estimates[1:6,1:5] #v 2 is a list
  })

ui.r

tableOutput("test2")

This provides the following error inside the app: 

Error: object of type 'closure' is not subsettable -- which makes me think it's not pulling the data


The assignment to output$test2 is not inside an eventReactive block. Does it need to be?

RE: providing more code. I'm happy to, if you can provide guidance as to what would be helpful. Putting together a minimally reproducible example is probably a chore for tomorrow though, not today.

Joe Cheng

unread,
Dec 31, 2015, 5:10:05 PM12/31/15
to Alex Whitworth, Shiny - Web Framework for R
Sounds like you're doing this:

dat$v2 <- eventReactive(...)

Don't do that. Instead do:

r2 <- eventReactive(...)

and then everywhere you want to read that value (like in renderTable) you call:

r2()

That "object of type 'closure' is not subsettable" usually means you forgot the (). Your example would probably work if you referred to dat$v2(), but there's really no reason at all to assign an event reactive to a reactive values object in this case.

And you never, never want to assign output$whatever inside an eventReactive block.

These are general rules that you should try hard to follow, I'll expand greatly on this in the new year:

0) Assigning things to output$xxx, writing things to disk, making a POST request, are examples of side effects. Reading from disk, calculating values, making a GET request, are not.
1) Anything with side effects should NEVER be done inside of reactive() or eventReactive(). reactive() and eventReactive() are used for calculating values, not performing actions.
2) Side effects that need to be performed reactively should always be done in observe() or observeEvent(). observe() and observeEvent() are always used for performing actions, as they don't return any values from the code that you put in them.
3) Assigning output$xxx <- renderXXX(...) should rarely be done inside of observe() or observeEvent(), and never in reactive() or eventReactive(). Generally they should be done directly within the Shiny Server function; anything dynamic about them (conditionals, etc.) should be performed *inside* the renderXXX(...). People sometimes mistakenly think of output$xxx <- renderXXX(...) as setting the output to a new value; that's not right. Instead, think of it as setting the recipe that is used to generate an xxx, and instructing Shiny to execute that recipe as often as is necessary.

This email doesn't do the topic justice but again, this is going to be a message I put a lot of effort into in early 2016. It'll be the subject of a tutorial at the Shiny conference which will be made available for download, and we'll have some kind of article or tutorial on it as well.

Malcolm Charles

unread,
Jan 15, 2016, 5:22:31 PM1/15/16
to Shiny - Web Framework for R, whitwor...@gmail.com
Hi Joe,

I've recently been using your SuperZip app as a starting point for an app of my own. After reading your post below, about proper use of reactive, events, etc., I realize I might have been thoughtlessly re-purposing your code.

I am trying to plot points on a map that are highly filterable by controls I provide in the UI - 4+ input$ variables. 

I saw your reactive expression zipsInBounds and thought that would be a good way to select the underlying dataset that would be plotted, e.g.:

   zerg <-reactive({
     test<-ifelse(input$group=="A" & input$year==1, return(groupA_y1),
            ifelse(input$group=="A" & input$year==0, return(groupA_y0),
                   ifelse(input$group=="B" & input$year==1, return(groupB_y1),
                          return(groupB_y0))))
     return(test)
   })
   

I had created those 4 data subsets in my Global.R script, and I'm using a nested ifelse statement to pull the correct one based on user input. Then I call this dataset into an Observe, where I am plotting the map:

  observe({
    dataset<- zerg()
    
    leafletProxy("mymap", data = dataset) %>% 

    .....


Is this the entirely wrong track to go down?

Joe Cheng

unread,
Jan 15, 2016, 6:56:33 PM1/15/16
to Malcolm Charles, Shiny - Web Framework for R, whitwor...@gmail.com
You're doing it right :) Your reactive has no side effects, check. Your side effecty operations (leafletProxy() %>% ...) are performed in an observe block, check.

I would change this though:

   zerg <-reactive({
     test<-ifelse(input$group=="A" & input$year==1, return(groupA_y1),
            ifelse(input$group=="A" & input$year==0, return(groupA_y0),
                   ifelse(input$group=="B" & input$year==1, return(groupB_y1),
                          return(groupB_y0))))
     return(test)
   })

to use normal if/else, and && instead of &:

zerg <- reactive({
  if (input$group=="A" && input$year==1)
    groupA_y1
  else if (input$group=="A" && input$year==0)
    groupA_y0
  else if (input$group=="B" && input$year==1)
    groupB_y0
  else
    NULL
})

ifelse() should only be used if you want vectorized results (i.e. you've got a whole vector of TRUE/FALSE values and want to choose from true and false result vectors by position).

Reply all
Reply to author
Forward
0 new messages