Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 68 additions & 2 deletions R/input-submit.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,54 @@
#' button do not automatically update their outputs when inputs change,
#' rather they wait until the user explicitly clicks the submit button.
#'
#' @section Warning:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to be a warning -- just having the text in the default section (details) should be fine.

#' Submit buttons are very particular shiny objects. One the weirdest
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a trimmed-down version that tells people right off the bat to use actionButtons instead:

Submit buttons are unusual Shiny inputs, and we recommend using \code{\link{actionButton}} instead of \code{submitButton} when you want to delay a reaction. See \href{http://shiny.rstudio.com/articles/action-buttons.html}{this article} for more information.

The presence of a submit button stops all inputs from sending their values automatically to the server, making them unwieldy for all but the simplest apps. If there are \emph{two} submit buttons in the same app, clicking either one will cause all inputs in the app to send their values to the server. Another issue with submit buttons is that dynamically created submit buttons (for example, with \code{\link{renderUI}} or \code{\link{insertUI}}) will not work.

#' things about them is they have no IDs, and there is no way to regulate
#' which inputs they should (and which ones they should not) be applied
#' to. This means they are very unwieldy for anything but the simplest
#' apps (see the example below). For example, having \emph{two} submit
#' buttons in the same app will probably not do what you'd expect because
#' submit buttons are not isolated in any way, so activating any of the
#' two buttons will cause all inputs in the app to fire. Another problem
#' with submit buttons is that dynamically created submit buttons (for
#' example, with \code{\link{renderUI}} or \code{\link{insertUI}}) will
#' not work.
#'
#' For all of these reasons, \strong{our general reccomendation is to
#' avoid submit buttons} and
#' \href{http://shiny.rstudio.com/articles/action-buttons.html#pattern-2---delay-reactions}{
#' use action buttons instead when you want to delay a reaction}. Any
#' code that uses a submit button can be converted to code that uses an
#' action button instead (while the reverse is almost never true). So,
#' if you find your app getting increasingly complicated, opt for using
#' an action button (or more since there is no problem with multiple
#' action buttons in the same app). The link above shows an example of
#' a delayed reaction using an action button. For another example, the
#' code below converts the example in this page (at the bottom) to an
#' equivalent app using an action button (using the same pattern
#' described in the link above):
#'
#' \preformatted{
#' shinyApp(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's best to leave out this example, because I don't think people will understand how it's a translation of the original submitButton version -- it's notably more complex, and uses a function (eventReactive) they're likely not familiar with. I think the people will be better served by reading the article. For the same reason, I also changed the article link (above) to point to the top level of the actionButton article instead of jumping to the eventReactive example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was also not feeling great about doing the "translation" right in the documentation. But since we're also moving the aside about submit buttons to the end of the Action Button article, do you think we could include both the example and the translation there (i.e. right at the end of the article)?

Copy link
Collaborator

@wch wch Nov 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that makes sense.

For simplicity, instead of using eventReactive(), you could just use isolate() in the renderPrint() call, like in the example below. However, looking at the article, I don't see this as one of the 5 patterns there. It is shown in the isolation article though.

@jcheng5, does it seem right to you that the action button article barely talks about using isolate()? That's the way I almost always use action buttons.

shinyApp(
  ui = basicPage(
    numericInput("num", label = "Make changes", value = 1),
    actionButton("update" ,"Update View", icon("refresh"),
      class = "btn btn-primary"),
    helpText("When you click the button above, you should see",
      "the output below update to reflect the value you",
      "entered at the top:"),
    verbatimTextOutput("value")
  ),
  server = function(input, output) {
    output$value <- renderPrint({ 
      req(input$update)
      isolate(input$num)
    })
  }
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that your version of the example looks simpler. I was using eventReactive() exactly because it matches up with Pattern 2 in the Action Button article. And I agree, Winston: this article should talk about isolate() more explicitly. Maybe we could do something like Pattern 2a and Pattern 2b: both delay reactions, but one uses eventReactive() and the other uses isolate()... Seems clunky, but for that pattern, I really do think these are equally valid (and the best one depends on the complexity of your app) -- for small things like this, isolate() is neater than introducing a special reactive for compute the value...

#' ui = basicPage(
#' numericInput("num", label = "Make changes", value = 1),
#' actionButton("update" ,"Update View", icon("refresh"),
#' class = "btn btn-primary"),
#' helpText("When you click the button above, you should see",
#' "the output below update to reflect the value you",
#' "entered at the top:"),
#' verbatimTextOutput("value")
#' ),
#' server = function(input, output) {
#' currentValue <- eventReactive(input$update, {
#' input$num
#' }, ignoreNULL = FALSE)
#'
#' output$value <- renderPrint({ currentValue() })
#' }
#' )
#' }
#'
#' @param text Button caption
#' @param icon Optional \code{\link{icon}} to appear on the button
#' @param width The width of the button, e.g. \code{'400px'}, or \code{'100\%'};
Expand All @@ -13,8 +61,26 @@
#' @family input elements
#'
#' @examples
#' submitButton("Update View")
#' submitButton("Update View", icon("refresh"))
#' if (interactive()) {
#'
#' shinyApp(
#' ui = basicPage(
#' numericInput("num", label = "Make changes", value = 1),
#' submitButton("Update View", icon("refresh")),
#' helpText("When you click the button above, you should see",
#' "the output below update to reflect the value you",
#' "entered at the top:"),
#' verbatimTextOutput("value")
#' ),
#' server = function(input, output) {
#'
#' # submit buttons do not have a value of their own,
#' # they control when the app accesses values of other widgets.
#' # input$num is the value of the number widget.
#' output$value <- renderPrint({ input$num })
#' }
#' )
#' }
#' @export
submitButton <- function(text = "Apply Changes", icon = NULL, width = NULL) {
div(
Expand Down
2 changes: 1 addition & 1 deletion inst/examples/07_widgets/Readme.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `submitButton`. The latter is used to delay rendering output until the user explicitly requests it.
This example demonstrates some additional widgets included in Shiny, such as `helpText` and `actionButton`. The latter is used to delay rendering output until the user explicitly requests it (a construct which also introduces two important server functions, `eventReactive` and `isolate`).
24 changes: 15 additions & 9 deletions inst/examples/07_widgets/server.R
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
library(shiny)
library(datasets)

# Define server logic required to summarize and view the
# Define server logic required to summarize and view the
# selected dataset
function(input, output) {

# Return the requested dataset
datasetInput <- reactive({

# Return the requested dataset. Note that we use `eventReactive()`
# here, which takes a dependency on input$update (the action
# button), so that the output is only updated when the user
# clicks the button.
datasetInput <- eventReactive(input$update, {
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
}, ignoreNULL = FALSE)

# Generate a summary of the dataset
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})

# Show the first "n" observations

# Show the first "n" observations. The use of `isolate()` here
# is necessary because we don't want the table to update
# whenever input$obs changes (only when the user clicks the
# action button).
output$view <- renderTable({
head(datasetInput(), n = input$obs)
head(datasetInput(), n = isolate(input$obs))
})
}
20 changes: 10 additions & 10 deletions inst/examples/07_widgets/ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@ library(shiny)

# Define UI for dataset viewer application
fluidPage(

# Application title.
titlePanel("More Widgets"),

# Sidebar with controls to select a dataset and specify the
# number of observations to view. The helpText function is
# also used to include clarifying text. Most notably, the
# inclusion of a submitButton defers the rendering of output
# inclusion of an actionButton defers the rendering of output
# until the user explicitly clicks the button (rather than
# doing it immediately when inputs change). This is useful if
# the computations required to render output are inordinately
# time-consuming.
sidebarLayout(
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),

numericInput("obs", "Number of observations to view:", 10),

helpText("Note: while the data view will show only the specified",
"number of observations, the summary will still be based",
"on the full dataset."),
submitButton("Update View")

actionButton("update", "Update View")
),

# Show a summary of the dataset and an HTML table with the
# requested number of observations. Note the use of the h4
# function to provide an additional header above each output
# section.
mainPanel(
h4("Summary"),
verbatimTextOutput("summary"),

h4("Observations"),
tableOutput("view")
)
Expand Down
71 changes: 69 additions & 2 deletions man/submitButton.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.