rstudio / shiny

Easy interactive web applications with R
https://shiny.posit.co/
Other
5.37k stars 1.87k forks source link

reset value of actionButton? #167

Closed stratosfmos closed 11 years ago

stratosfmos commented 11 years ago

can an actionbutton be reseted to 0 inside server.R? let's say i have actionButton("b1","button1"). what could be the code for reseting the action button to 0?

jcheng5 commented 11 years ago

No, not at this time. Can you talk a little about why you want this? Thanks...

stratosfmos commented 11 years ago

i was trying to bypass the issue with the radiobuttons(and the updateradiobuttons).so here is the logic. We have 2 buttons lets say b1 and b2. when a button is pushed then it is increased by 1 in server.R we render some text for this action.after it, the button must reseted to 0 so that if the state of a button is 1 it must trigger some text rendering and etc. but i think you gave me the answer. but it would be a nice addition for some ui control enhancement.what's your opinion?

jcheng5 commented 11 years ago

Don't think of the value of the input$actionButtonId as being meaningful, except for the special case of 0, which means "the button hasn't been pressed yet". In all other cases, you should just rely on the fact that input$actionButtonId changes and invalidates your reactive expression or observer. If you find yourself checking the actual value of input$actionButtonId (other than testing for 0) then it's a good sign you probably need to look again at using isolate:

# Take an action when button is clicked
observe({
  if (input$actionButtonId == 0)
    return()
  isolate({
    # Your logic here
  })
})

# Render text when button is clicked
output$text <- renderText({
  if (input$actionButtonId == 0)
    return("")
  isolate({
    # Your logic here
  })
})
xiaodaigh commented 10 years ago

Actually I think the reset is useful. I have an app that contains multiple projects. E.g. each project could be a different data set I am analysing, so when I switch projects I would want to reset all buttons as if they have never been pressed.

sgrevers commented 10 years ago

I have the exact same problem where resetting the actionbuttons would help alot. An user is able to switch locations / working directories where he can analyse & create plots for different data sets. For all these different data sets the same plot buttons are being used. As soon as you would change the working directory now all scripts get called because buttons have been pressed already.

sarvagna commented 10 years ago

I'm facing this issue too. I have two buttons add and clear to build a query. when i select something and say add, that term should be added to query and if click on clear button, it should clear the query. i'm checking for add_button>0 now but that won't really solve my purpose because when i select something else after clicking add button it'll automatically be added to the query. i tried resetting the actionbutton value but really could not.

sgrevers commented 10 years ago

i did an odd workaround instead, using a var which i reset to 0 as soon as the button is pressed. I'm using input$h5fileName so the user can load another file.

observe ({
    input$h5fileName
    counter_createTime_Plot <<- 0
})

when call the function i want to run i increment this ensuring its run every time i actually press the actionButton.

observe({
    # increment the counter so i know it has been pressed
    if (counter_createTime_Plot == 0)
      counter_createTime_Plot <<- counter_createTime_Plot + 1

    #returning when the ui.R is opened the first time
    if (input$createTime_Plot == 0)
      return()

    # run the function because the button is clicked once after loading a new file
    else if (counter_createTime_Plot == 1) {
})

I use the counter value 1 to do a check if the file is correct, using a next if statement with if (counter_createTime_Plot > 1) to use the same actionButton for doing something else. It's not a pretty solution but for now it works the way i want it to. Being able to reset would still be much better.

jcheng5 commented 10 years ago

I still don't think you need this. If your add logic is surrounded by isolate you shouldn't have this problem. If I'm wrong, let me know.

sgrevers commented 10 years ago

in sarvagna's case most likely yes, since i suspect his input field is in the same observer as both actionButtons (maybe share the code?). In my case however i have a button which has to check the data input when it is clicked the first time, when clicking it for the second time it should not check the datainput but create a plot instead. A third click would do nothing since the data is validated and the plot has been made.

The user is also able to switch files however and as soon as he/she does the plot button became useless. In this situation it would be perfect if i could just reset the actionbutton instead when a a different file is loaded. Now i have to keep track how many times it is clicked with an external counter variable.

sarvagna commented 10 years ago

Here is my code, i tried to use the logic you mentioned. this works well first time i click either add or clear button. but after that the buttons are useless.

observe({
 if (input$add_button == 0)
   return()
   isolate({
  selected_term_ids<-c(input$thti,input$select_child_terms)
  if((!is.null(selected_term_ids)) & length(selected_term_ids)>0){
    onto <- input$search_option
  selected_terms <-    get_onto_terms_by_ids(paste0(selected_term_ids,collapse=","))
 } 
 if(!is.null(DisplayString$names)){
   DisplayString$names<-unique(append(DisplayString$names,selected_terms$term))
   QueryString$names<-unique(append(QueryString$names,selected_term_ids))
 }
 else{
   DisplayString$names<-selected_terms$term
   QueryString$names<-selected_term_ids
 }
 output$dynamic_value <- renderText({
   paste0(DisplayString$names, collapse=",")
 })
  })
if(input$clear_button == 0)
  return()
  isolate({
 DisplayString$names<- NULL
 QueryString$names <- NULL
 output$dynamic_value <- renderText({DisplayString$names})
})
 })
jcheng5 commented 10 years ago

Try using two observers, one for each button. Also, what are DisplayString and QueryString? Just normal lists or reactiveValues objects?

sarvagna commented 10 years ago

they are reactiveValues objects. I'll be using the displaystring in verbatimtextoutput and querystring in db query function further down the code

jcheng5 commented 10 years ago

In that case output$dynamic_value only needs to be assigned once, outside of any observer.

sarvagna commented 10 years ago

Well i did something like this and it seems to be working for now.

#Build query
  DisplayString <- reactiveValues()
  QueryString <- reactiveValues()

  observe({
    if (input$add_button > 0){
      isolate({
        selected_term_ids<-c(input$thti,input$select_child_terms)
        if((!is.null(selected_term_ids)) & length(selected_term_ids)>0){
          onto <- input$search_option
        selected_terms <- get_onto_terms_by_ids(paste0(selected_term_ids,collapse=","))
        } 
        if(!is.null(DisplayString$names)){
          DisplayString$names<-unique(append(DisplayString$names,selected_terms$term))
          QueryString$names<-unique(append(QueryString$names,selected_term_ids))
        }
        else{
          DisplayString$names<-selected_terms$term
          QueryString$names<-selected_term_ids
        }
      })
    }
#Clear selection
if(input$clear_button >= input$add_button){
  isolate({
    DisplayString$names<- NULL
    QueryString$names <- NULL
  })
}
  })

  #Show selected terms
  output$dynamic_value <- renderText({
    paste0(DisplayString$names, collapse=",")
  })
sharko666 commented 10 years ago

I really wish one could reset an actionButton to 0.

jcheng5 commented 10 years ago

@sharko666 Let me know if you have a simple example of why you need that. I'm totally open to being convinced but haven't seen any scenarios so far that weren't better handled using the idioms we've already established.

Marccohen commented 10 years ago

Joe, Did you send this to me by mistake? I'm not sharko666.

On Mon, Oct 13, 2014 at 11:29 AM, Joe Cheng notifications@github.com wrote:

@sharko666 https://github.com/sharko666 Let me know if you have a simple example of why you need that. I'm totally open to being convinced but haven't seen any scenarios so far that weren't better handled using the idioms we've already established.

— Reply to this email directly or view it on GitHub https://github.com/rstudio/shiny/issues/167#issuecomment-58934438.

Marc-david Cohen | Chief Science Officer | +1.415.205.6295 | www.aktana.com

The power of many, the focus of one

wch commented 10 years ago

@Marccohen You probably got a copy of the message because you're watching the shiny repository on github.

sharko666 commented 10 years ago

Hi Joe, I'm working on a application which takes an URL into a text field and loads data sets from an URL. That works fine as long as the user copy and pastes the URL into the text field. If there is a mistake in the URL or it is typed in, shiny constantly tries to load data from an unfinished URL. That's why I thought I add a submit button to it. That improved things that now the URL also can be typed in as nothing happens as long as the button is pressed. The new problem arises when another URL after the first one is typed in. The counter is already higher than 0 so the button is marked as pressed. So this works only for the first time. This problem would have been easily solved if there would be a way to reset the counter in the logic part when the dataset is loaded or an appropriate error (if the URL is still wrong) is caught.

Now, I found a solution but it might not be the solution as shiny is supposed to work. I have a global variable set in the reactiveValues function called actionCounter. Every time the button is pressed the buttoncounter is increased and is higher than the actionCounter. Inside the logic the action counter is set to the buttoncounter. Now I only test whether the actionCounter is lower than the buttonCounter. See code below.

server.R

vars = reactiveValues(actionCounter=0)

inside the observer for the textfield

url = input$URL_input if(!is.null(input$submit_URL)){ if(input$submit_URL>vars$actionCounter&url!=""){ vars$actionCounter=input$submit_URL

try load the data

} }

So there is a relative easy solution but if there were the possibility to reset the button counter it would have been simpler and easier to understand.

Regards

Christoph


From: Marc Cohen [notifications@github.com] Sent: Tuesday, October 14, 2014 7:37 To: rstudio/shiny Cc: Christoph Knapp Subject: Re: [shiny] reset value of actionButton? (#167)

Joe, Did you send this to me by mistake? I'm not sharko666.

On Mon, Oct 13, 2014 at 11:29 AM, Joe Cheng notifications@github.com wrote:

@sharko666 https://github.com/sharko666 Let me know if you have a simple example of why you need that. I'm totally open to being convinced but haven't seen any scenarios so far that weren't better handled using the idioms we've already established.

— Reply to this email directly or view it on GitHub https://github.com/rstudio/shiny/issues/167#issuecomment-58934438.

Marc-david Cohen | Chief Science Officer | +1.415.205.6295 | www.aktana.com

The power of many, the focus of one

— Reply to this email directly or view it on GitHubhttps://github.com/rstudio/shiny/issues/167#issuecomment-58935448.

jcheng5 commented 10 years ago

Thanks for the feedback. This is the idiomatic way to handle such a situation:

url <- reactive({
  if (input$submit_URL == 0) {
    return(NULL)
  }
  isolate(input$URL_input)
})

Then everywhere you want to access the URL, use url().

It is a longstanding grievance against Shiny that we don't have wrapper functions for this idiom.

jcheng5 commented 10 years ago

@sharko666 I just merged a very longstanding pull request into master. With this change you can now do this instead:

url <- reactive({
  eventFilter(input$submit_URL, input$URL_input)
})

This basically means "the value of url is nothing (NULL) at first, but every time input$submit_URL is clicked, set it to the then-current value of input$URL_input".

There is a complementary observeEvent function as well:

observeEvent(input$submit_URL, function() {
  # Do something involving input$URL_input
})

The "do something" part of the code will be executed when and only when the button is clicked.

dakshvarma commented 10 years ago

@jcheng5 I just used the new version of Shiny - 10.2.9 for the new observeEvent function. It works great on my desktop version of R studio, but I am trying to run this using a portable R and Chrome install from a flash drive. This worked fine with shiny 10.2.1 because I was able to find a zip that worked for shiny 10.2.1. I have tried going though github, but am not sure how I can find (or create) a 10.2.9 zip that will work for a portable (32 bit) install of R. If you could point be in the right direction that would be much appreciated. Thanks :)

jcheng5 commented 10 years ago

@dakshvarma Is there a reason that devtools::install_github` wouldn't work with your portable R?

dakshvarma commented 10 years ago

@jcheng5 Got it working finally! devtools::install_github installs it by creating a local folder. The trick is to zip up the local folder, install a package from the zip in the portable install (with the zip in the same folder as portable R). Thanks for your help!

hrasof commented 9 years ago

i have the following case: 2 actionsbuttons (Generate and Reset) that should be used under ONE renderPlot. How to handle this situation? Appreciate your help

output$Result <- renderPlot({
  if(input$GEN_button == 0){
    # mylogic
  }
  else if(input$RES_button == 0){
    # mylogic
  }
  else {
    # mylogic
  }
})  
jcheng5 commented 9 years ago

What does Reset do, clear it? What should the initial state of the plot be--should it be generated when the app first starts up, or should it be blank?

On Tue, Nov 18, 2014 at 8:37 AM, Hratch notifications@github.com wrote:

i have the following case: 2 actionsbuttons (Generate and Reset) that should be used under ONE renderPlot. How to handle this situation? Appreciate your help

output$Result <- renderPlot({ if(input$GEN_button == 0){

mylogic

} else if if(input$RES_button == 0){

mylogic

} else {

mylogic

} })

— Reply to this email directly or view it on GitHub https://github.com/rstudio/shiny/issues/167#issuecomment-63500459.

hrasof commented 9 years ago

Reset sets back the value of the matrix to be plotted to an initial value. The initial state (Initial run) should show plot the matrix that is created within the logic. If Generate Button is clicked then a new matrix will be generated and plotted.

That means the matrix can be calculated outside the renderPlot too right?

jcheng5 commented 9 years ago

In that case, it sounds like both Generate at Reset should affect not the plot directly, but a matrix value.

initialMatrixValue <- ...
shinyServer(function(input, output, session) {
  values <- reactiveValues(matrix = initialMatrixValue)
  observe({
    if (input$GEN_button == 0)
      return()
    values$matrix <- ... # Generate the matrix here
  })
  observe({
    if (input$RES_button == 0)
      return()
    values$matrix <- initialMatrixValue
  })
  output$Result <- renderPlot({
    plot(values$matrix) # or whatever
  })
})
hrasof commented 9 years ago

Thank you for your fast feedback/solution, you're great.

shanthiviswanathan commented 9 years ago

I am trying to do something similar and have multiple issues:

  1. I accept a phrase, I process the phrase and predict a word, I add the predicted word back into the input. But this causes an infinite loop. Is there a way to fix this?
  2. Then I created a textbox for the predicted word and then added a button "Accept". If user presses Accept, the word gets appended to the existing phrase. However, I cannot get this to work. The code is below:

ui.R: textInput("word", "Phrase", value=""), textInput("predictedword", "Next Word:", value=""), actionButton("accept", "Accept")

server.R shinyServer( function(input, output, session) { observe ({ if (input$accept > 0) { predicted <- paste(input$word, input$predictedword) updateTextInput(session, "word", value = predicted) } else { return() } })

observe({
  nextword <- toString(predict(input$word,all.gram,4))
  updateTextInput(session, "predictedword", value = nextword)
})  

})

jcheng5 commented 9 years ago

The infinite loop can be fixed by adding the function in bold:

if (input$accept > 0) {

This is almost always the pattern you want to follow when using actionButton. In fact we have some features coming up in Shiny that will wrap this pattern up for you so it's more natural to follow.

See http://shiny.rstudio.com/articles/isolation.html for more details.

On Thu, Nov 20, 2014 at 11:27 AM, Shanthi notifications@github.com wrote:

I am trying to do something similar and have multiple issues:

  1. I accept a phrase, I process the phrase and predict a word, I add the predicted word back into the input. But this causes an infinite loop. Is there a way to fix this?
  2. Then I created a textbox for the predicted word and then added a button "Accept". If user presses Accept, the word gets appended to the existing phrase. However, I cannot get this to work.

ui.R: textInput("word", "Phrase", value=""), textInput("predictedword", "Next Word:", value=""), actionButton("accept", "Accept")

server.R shinyServer( function(input, output, session) { observe ({ if (input$accept > 0) { predicted <- paste(input$word, input$nextword) updateTextInput(session, "word", value = predicted) } else { return() } })

observe({ nextword <- toString(predict(input$word,all.gram,4)) updateTextInput(session, "predictedword", value = nextword) })

})

— Reply to this email directly or view it on GitHub https://github.com/rstudio/shiny/issues/167#issuecomment-63864397.

shanthiviswanathan commented 9 years ago

Cool, that worked very well. Thanks for your quick response.

shanthiviswanathan commented 9 years ago

Is it possible to put the cursor at the end of the text in the above scenario? Basically, when the user clicks "Accept", I add the text to the input text but I want to place the cursor at the end.

jcheng5 commented 9 years ago

This has to be done with JS. Try including this in your UI.

tags$script(HTML(" Shiny.addCustomMessageHandler('cursorEnd', function() { var input = $('#word'); input[0].selectionStart = input[0].selectionEnd = input.val().length; }); "))

Right after calling updateTextInput(session, "word", ...), you should add: session$sendCustomMessage("cursorEnd", NULL)

On Thu, Nov 20, 2014 at 12:55 PM, Shanthi notifications@github.com wrote:

Is it possible to put the cursor at the end of the text in the above scenario? Basically, when the user clicks "Accept", I add the text to the input text but I want to place the cursor at the end.

— Reply to this email directly or view it on GitHub https://github.com/rstudio/shiny/issues/167#issuecomment-63878009.

shanthiviswanathan commented 9 years ago

Joe - this does not do anything. My ui.R and server.R are below: library(shiny) library(stringi) library(data.table)

shinyUI(fluidPage( titlePanel("TextPredictor"), fluidRow( column(6, wellPanel(
textInput("word", "Phrase", value=""), tags$style(type='text/css', "#word { width: 300px; }"), tags$script(HTML(" Shiny.addCustomMessageHandler('cursorEnd', function() { var input = $('#word'); input[0].selectionStart = input[0].selectionEnd = input.val().length; }); ")) , actionButton("clear", "Clear"), textInput("predictedword", "Next Word:", value=""), actionButton("accept", "Accept") )) )))

server.R shinyServer( function(input, output, session) { observe ({ if (input$accept > 0) { isolate({ phrase <- clean(input$word) predicted <- paste(phrase, input$predictedword) updateTextInput(session, "word", value = predicted) session$sendCustomMessage("cursorEnd", NULL) }) } })

observe ({
  if (input$clear > 0) {
    isolate({
      updateTextInput(session, "word", value = "")
    })
  }
})

observe({
  phrase <- clean(input$word)
  nextword <- toString(predict(phrase,all.gram,4))
  updateTextInput(session, "predictedword", value = nextword)
})  

})

jcheng5 commented 9 years ago

Sorry, two mistakes:

Shiny.addCustomMessageHandler('cursorEnd', function() { should be Shiny.addCustomMessageHandler('cursorEnd', function(message) {

and

session$sendCustomMessage("cursorEnd", NULL) should be session$sendCustomMessage("cursorEnd", TRUE)

On Thu, Nov 20, 2014 at 2:50 PM, Shanthi notifications@github.com wrote:

Joe - this does not do anything. My ui.R and server.R are below: library(shiny) library(stringi) library(data.table)

shinyUI(fluidPage( titlePanel("TextPredictor"), fluidRow( column(6, wellPanel(

textInput("word", "Phrase", value=""), tags$style(type='text/css', "#word { width: 300px; }"), tags$script(HTML(" Shiny.addCustomMessageHandler('cursorEnd', function() { var input = $('#word'); alert(input) input[0].selectionStart = input[0].selectionEnd = input.val().length; }); ")) , actionButton("clear", "Clear"), textInput("predictedword", "Next Word:", value=""), actionButton("accept", "Accept") )) )))

server.R shinyServer( function(input, output, session) { observe ({ if (input$accept > 0) { isolate({ phrase <- clean(input$word) predicted <- paste(phrase, input$predictedword) updateTextInput(session, "word", value = predicted) session$sendCustomMessage("cursorEnd", NULL) }) } })

observe ({ if (input$clear > 0) { isolate({ updateTextInput(session, "word", value = "") }) } })

observe({ phrase <- clean(input$word) nextword <- toString(predict(phrase,all.gram,4)) updateTextInput(session, "predictedword", value = nextword) })

})

— Reply to this email directly or view it on GitHub https://github.com/rstudio/shiny/issues/167#issuecomment-63894797.

shanthiviswanathan commented 9 years ago

Pretty neat solution, thanks once again. I did not even know these methods exist in Shiny.

shanthiviswanathan commented 9 years ago

I have a new problem. I am able to append the text and set the cursor, however, the cursor is not visible. I always see the first part of the text and the text does not scroll to the right.

I converted text into text area, and see the same issue. How can I bring in focus to the end of the text area? Thanks

dhassouni commented 9 years ago

I feel as though a simple case has been overlooked (and forgive me if I somehow missed this example in the above thread). It is very common to have a set of checkboxes with one button to check all, and another to uncheck all. For example, the desired code from server.R below. While I can conceive of a few ways to get around this, it doesn't mean that you should not be able to reset to 0. The reason the code below does not work is that after selecting and deselecting once, both input action button values are greater than 0. I am not deriving anything meaningful from the increment counter other than "the special value of 0" as stated above. Thoughts on this example?

observe({ if (input$uncheckAll > 0 ) { updateCheckboxGroupInput(session = session, inputId = "test", choices = c(1,2,3), selected = NULL) } if (input$checkAll > 0 ) { updateCheckboxGroupInput(session = session, inputId = "test", choices = c(1,2,3), selected = c(1,2,3)) } })

jcheng5 commented 9 years ago

These just need to be two separate observers.

observe({
  if (input$uncheckAll > 0 ) {
    updateCheckboxGroupInput(session = session, inputId = "test", choices = c(1,2,3), selected = NULL)
  }
})
observe({
  if (input$checkAll > 0 ) {
    updateCheckboxGroupInput(session = session, inputId = "test", choices = c(1,2,3), selected = c(1,2,3))
  }
})

Actually with recent versions of Shiny it's nicer to use observeEvent, and also you don't need to provide the choices if they're not changing:

observeEvent(input$uncheckAll, {
  updateCheckboxGroupInput(session, "test", selected = NULL)
})
observeEvent(input$checkAll, {
  updateCheckboxGroupInput(session, "test", selected = c(1,2,3))
})
dhassouni commented 9 years ago

Thanks much, this actually makes sense and I stand corrected. I still don't quite understand the resistance to not being able to reset to zero - it doesn't seem to inherently promote any bad coding practices, and just because something can be done one way doesn't mean there may not be a better or more intuitive way. Anyhow, you have come up with elegant solutions for most of the problems here, so for now this will definitely suffice. Also, thank you for the advice about observeEvent(). I wasn't even aware of this new construct (I'm relatively new to shiny, and few of the examples in the gallery seem to use some of the newer functionality I am coming across).

jcheng5 commented 9 years ago

I still don't quite understand the resistance to not being able to reset to zero - it doesn't seem to inherently promote any bad coding practices

It absolutely, positively does promote bad coding practices. Just look at all of the code examples in this thread (through no fault of the users, of course--we just haven't done a good enough job with education). The action button being clicked should be thought of as a discrete event, not a value. The special 0 value is just a workaround to prevent the code from thinking that the button is clicked at startup. It's the fact that a number has changed that is the relevant signal here, not the value that the number happens to be.

Shiny's reactivity system wasn't designed for imperative programming--and pushing a button is, by nature, imperative. It was a stroke of pure luck that I came up with the observe/isolate idiom that allows actionButtons to work so nicely. It is a very narrow pattern that has very broad applications; if you stay within the pattern you can accomplish almost anything safely. If you stray outside the pattern your program is suddenly very difficult to reason about AND probably also doesn't accomplish what you originally wanted it to.

I think part of the reason is that the observe/isolate examples don't LOOK like anything particularly special relative to other reactive code. I'm hoping that observeEvent will help make the intent clearer, and make it less tempting to abuse.

dhassouni commented 9 years ago

I still disagree with this premise. There is a difference between promoting bad coding practices and people using code in the wrong way. Almost any code construct can be used inappropriately. I think the root of this disagreement may be the implementation of the counter for the actionButton, which I might argue is not intuitive based on many older examples I've found using observe. I will readily grant that that the interface for observeEvent is much cleaner and will naturally obfuscate the value associated with the actionButton. In any case, I do very much thank you for your help. If you want to continue the conversation about promotion of bad coding practices, feel free to email me as I don't want this thread to get too far off topic. Of course, if it relates to the examples and use cases, definitely post here. Thanks again.

dhassouni commented 9 years ago

quick side note. In my uncheckAll example, I used selected = NULL within updateCheckboxGroupInput. According to the documentation for this method, any arguments with a NULL value will be ignored. Hence, for those looking at this example, you must use selected = c('') or some such equivalent.

MartinNowack commented 9 years ago

Hi, i think there is currently one problem with the approach: if you deleting and adding the button with the same ID again, the input$ID might still exist and therefore is not null anymore, therefore the function gets executed immediately when added, I worked around that issue by extending the observeEvent function the following way (in a nutshell: I get the old value first and then compare with the new event):

observeEvent2 <- function (eventExpr, handlerExpr, event.env = parent.frame(),
          event.quoted = FALSE, handler.env = parent.frame(), handler.quoted = FALSE,
          label = NULL, suspended = FALSE, priority = 0, domain = getDefaultReactiveDomain(),
          autoDestroy = TRUE, ignoreNULL = TRUE)
{
  eventFunc <- exprToFunction(eventExpr, event.env, event.quoted)
  if (is.null(label))
    label <- sprintf("observeEvent(%s)", paste(deparse(body(eventFunc)),
                                               collapse = "\n"))
  handlerFunc <- exprToFunction(handlerExpr, handler.env,
                                handler.quoted)

  oldValue <- isolate(eventFunc())

  invisible(observe({
    e <- eventFunc()
    if (ignoreNULL && (isNullEvent(e) || (!is.null(oldValue) && e == oldValue))) {
      return()
    }
    isolate(handlerFunc())
  }, label = label, suspended = suspended, priority = priority,
  domain = domain, autoDestroy = TRUE))
}

It's still not perfect but works much better.

nhpackard commented 9 years ago

I, too, find myself wanting to reset an action button. I don't find the following functionality in the thread so far, so I'll try it out for Joe...

It has to do with using a conditional panel that looks at an action button value for its condition (this is in ui.R, not server.R).

In a sidePanel I have something like:

sidePanel(
...
actionButton(doit,"Configure this variable")
...
)

then in a main panel I have something like

mainPanel(
   ...
   # for action button not pushed:
   conditionalPanel(condition="input.doit == 0 & input.tabs==...",
                                ...),
   ...
   # for action button pushed:
   conditionalPanel(condition="input.doit > 0 & input.tabs==...",
                                ...),

Now, at some point (maybe triggered by another action button, I would like to reset input.doit and re-display the first conditional panel, have the user re-specify inputs there, etc. Seems like it would be quite natural to have something like updateActionButton() over in server.R to get me back to my first conditional panel.

I would be very happy to hear the 'right and proper' way to do this, given that updateActionButton() does not exist.

irJoostvanVeen commented 9 years ago

To get a 0 / 1 actionbutton without resetting:

attr(input, "readonly") <- FALSE
input$ActionButtonMemory <- 0
observe({
  if(length(input$ActionButton)>0){
  if((input$ActionButton-input$ActionButtonMemory)>0){
    input$ActionButtonMemory<- input$ActionButton # Equalize
    # DO YOUR THING HERE
  }}
})
mikebirdgeneau commented 8 years ago

I stumbled upon this issue, while looking for a way to write a single handler that can respond to a large number of individual (dynamically changing) action buttons. Here's an example of what I was trying to do:

save_settings <- function()
  {
    xs <- seq(1,100,by=1)
    result<-lapply(xs,function(x){
      observeEvent(input[[paste0(x,"_save")]], {
            if(!(input[[paste0(x,"_save")]]==0)){
        # Save something for this value of x.
        #input[[paste0(x,"_save")]] <- 0 # Could be a nice place to be able to reset to 0.
            }
          }
        }
        message(paste0(x,"_save: ",input[[paste0(x,"_save")]]))
      })
      input[[paste0(x,"_save")]]
    })
    return(result)
  }
})

I could see the 'reset' to zero being useful in this case, but perhaps there's a better way of doing this. Has anyone done something similar?

jcheng5 commented 8 years ago

@mikebirdgeneau That's absolutely not necessary in this case. This is all you need to do:

save_settings <- function() {
  xs <- seq(1,100,by=1)
  lapply(xs,function(x){
    observeEvent(input[[paste0(x,"_save")]], {
      # Save something for this value of x.
    })
  })
}
mikebirdgeneau commented 8 years ago

@jcheng5 thanks - I'll give that a go! Appreciate the quick response. Cheers.