From 976818f55c28875ef79528ac174c797ebd3f29d2 Mon Sep 17 00:00:00 2001 From: Winston Chang Date: Thu, 21 Feb 2013 15:35:13 -0600 Subject: [PATCH] Convert all reactiveXX to renderXX and convert reactive functions to expressions --- _includes/tutorial/building-outputs.md | 41 ++++++++++++++-------- _includes/tutorial/downloads.md | 6 ++-- _includes/tutorial/dynamic-ui.md | 18 +++++----- _includes/tutorial/hello-shiny.md | 10 +++--- _includes/tutorial/html-ui.md | 16 ++++----- _includes/tutorial/inputs-and-outputs.md | 16 ++++----- _includes/tutorial/more-widgets.md | 6 ++-- _includes/tutorial/reactivity.md | 44 ++++++++++++------------ _includes/tutorial/scoping.md | 2 +- _includes/tutorial/shiny-text.md | 14 ++++---- _includes/tutorial/sliders.md | 6 ++-- _includes/tutorial/tabsets.md | 18 +++++----- 12 files changed, 104 insertions(+), 93 deletions(-) diff --git a/_includes/tutorial/building-outputs.md b/_includes/tutorial/building-outputs.md index d94065e04..2c0c4e516 100644 --- a/_includes/tutorial/building-outputs.md +++ b/_includes/tutorial/building-outputs.md @@ -8,26 +8,31 @@ Similar to custom inputs, if you have some knowle Start by deciding the kind of values your output component is going to receive from the user's server side R code. -Whatever value the user's R code returns is going to need to somehow be turned into a JSON-compatible value (Shiny uses [RJSONIO](http://cran.r-project.org/web/packages/RJSONIO/index.html) to do the conversion). If the user's code is naturally going to return something RJSONIO-compatible – like a character vector, a data frame, or even a list that contains atomic vectors – then you can just direct the user to use a regular `reactive` function on the server. However, if the output needs to undergo some other kind of transformation, then you'll need to write a wrapper function for `reactive` that your users will use instead (analogous to `reactivePlot` or `reactiveTable`). +Whatever value the user's R code returns is going to need to somehow be turned into a JSON-compatible value (Shiny uses [RJSONIO](http://cran.r-project.org/web/packages/RJSONIO/index.html) to do the conversion). If the user's code is naturally going to return something RJSONIO-compatible – like a character vector, a data frame, or even a list that contains atomic vectors – then you can just direct the user to use a function on the server. However, if the output needs to undergo some other kind of transformation, then you'll need to write a wrapper function that your users will use instead (analogous to `renderPlot` or `renderTable`). -For example, if the user wants to output [time series objects](http://stat.ethz.ch/R-manual/R-patched/library/stats/html/ts.html) then you might create a `reactiveTimeSeries` function that knows how to translate `ts` objects to a simple list or data frame: +For example, if the user wants to output [time series objects](http://stat.ethz.ch/R-manual/R-patched/library/stats/html/ts.html) then you might create a `renderTimeSeries` function that knows how to translate `ts` objects to a simple list or data frame: -
reactiveTimeSeries <- function(func) {
-    reactive(function() {
-        val <- func()
-        list(start = tsp(val)[1],
-             end = tsp(val)[2],
-             freq = tsp(val)[3],
-             data = as.vector(val))
-    })
-}
+ +{% highlight r %} +renderTimeSeries <- function(expr, env=parent.frame(), quoted=FALSE) { + # Convert the expression + environment into a function + func <- exprToFunction(expr, env, quoted) + + val <- func() + list(start = tsp(val)[1], + end = tsp(val)[2], + freq = tsp(val)[3], + data = as.vector(val)) +} +{% endhighlight %} which would then be used by the user like so: -
output$timeSeries1 <- reactiveTimeSeries(function() {
+{% highlight r %}
+output$timeSeries1 <- renderTimeSeries(function() {
     ts(matrix(rnorm(300), 100, 3), start=c(1961, 1), frequency=12)
 })
-
+{% endhighlight %} ### Design Output Component Markup @@ -35,7 +40,9 @@ At this point, we're ready to design the HTML markup and write the JavaScript co For many components, you'll be able to have extremely simple HTML markup, something like this: -
<div id="timeSeries1" class="timeseries-output"></div>
+{% highlight html %} +
+{% endhighlight %} We'll use the `timeseries-output` CSS class as an indicator that the element is one that we should bind to. When new output values for `timeSeries1` come down from the server, we'll fill up the div with our visualization using JavaScript. @@ -90,6 +97,10 @@ An output binding object needs to have the following methods: ### Register Output Binding Once you've created an output binding object, you need to tell Shiny to use it: -
Shiny.outputBindings.register(exampleOutputBinding, "yourname.exampleOutputBinding");
+ +{% highlight javascript %} +Shiny.outputBindings.register(exampleOutputBinding, "yourname.exampleOutputBinding"); +{% endhighlight %} + The second argument is a string that uniquely identifies your output binding. At the moment it is unused but future features may depend on it. diff --git a/_includes/tutorial/downloads.md b/_includes/tutorial/downloads.md index ce016b6d7..99f7f315b 100644 --- a/_includes/tutorial/downloads.md +++ b/_includes/tutorial/downloads.md @@ -25,14 +25,14 @@ You define a download using the `downloadHandler` function on the server side, a #### server.R
shinyServer(function(input, output) {
-  datasetInput <- reactive(function() {
+  datasetInput <- reactive({
     switch(input$dataset,
            "rock" = rock,
            "pressure" = pressure,
            "cars" = cars)
   })
   
-  output$table <- reactiveTable(function() {
+  output$table <- renderTable({
     datasetInput()
   })
   
@@ -49,6 +49,6 @@ As you can see, `downloadHandler` takes a `filename` argument, which tells the w
 
 The `content` argument must be a function that takes a single argument, the file name of a non-existent temp file. The `content` function is responsible for writing the contents of the file download into that temp file.
 
-Both the `filename` and `content` arguments can use reactive values and functions (although in the case of `filename`, be sure your argument is an actual function; `filename = paste(input$dataset, '.csv')` is not going to work the way you want it to, since it is evaluated only once, when the download handler is being defined).
+Both the `filename` and `content` arguments can use reactive values and expressions (although in the case of `filename`, be sure your argument is an actual function; `filename = paste(input$dataset, '.csv')` is not going to work the way you want it to, since it is evaluated only once, when the download handler is being defined).
 
 Generally, those are the only two arguments you'll need. There is an optional `contentType` argument; if it is `NA` or `NULL`, Shiny will attempt to guess the appropriate value based on the filename. Provide your own content type string (e.g. `"text/plain"`) if you want to override this behavior.
\ No newline at end of file
diff --git a/_includes/tutorial/dynamic-ui.md b/_includes/tutorial/dynamic-ui.md
index d9d28d121..e54978234 100644
--- a/_includes/tutorial/dynamic-ui.md
+++ b/_includes/tutorial/dynamic-ui.md
@@ -5,7 +5,7 @@ Shiny apps are often more than just a fixed set of controls that affect a fixed
 Shiny currently has three different approaches you can use to make your interfaces more dynamic. From easiest to most difficult, they are:
 
 * **The `conditionalPanel` function**, which is used in `ui.R` and wraps a set of UI elements that need to be dynamically shown/hidden
-* **The `reactiveUI` function**, which is used in `server.R` in conjunction with the `htmlOutput` function in `ui.R`, lets you generate calls to UI functions and make the results appear in a predetermined place in the UI
+* **The `renderUI` function**, which is used in `server.R` in conjunction with the `htmlOutput` function in `ui.R`, lets you generate calls to UI functions and make the results appear in a predetermined place in the UI
 * **Use JavaScript** to modify the webpage directly.
 
 Let's take a closer look at each approach.
@@ -26,7 +26,7 @@ conditionalPanel(
 
 In this example, the select control for `smoothMethod` will appear only when the `smooth` checkbox is checked. Its condition is `"input.smooth == true"`, which is a JavaScript expression that will be evaluated whenever any inputs/outputs change.
 
-The condition can also use `output` values; they work in the same way (`output.foo` gives you the value of the output `foo`). If you have a situation where you wish you could use an R expression as your `condition` argument, you can create a reactive function in `server.R` and assign it to a new output, then refer to that output in your `condition` expression. For example:
+The condition can also use `output` values; they work in the same way (`output.foo` gives you the value of the output `foo`). If you have a situation where you wish you could use an R expression as your `condition` argument, you can create a reactive expression in `server.R` and assign it to a new output, then refer to that output in your `condition` expression. For example:
 
 #### ui.R
 
@@ -39,24 +39,24 @@ conditionalPanel(
 #### server.R
 
 
# Partial example
-datasetInput <- reactive(function() {
+datasetInput <- reactive({
    switch(input$dataset,
           "rock" = rock,
           "pressure" = pressure,
           "cars" = cars)
 })
 
-output$nrows <- reactive(function() {
+output$nrows <- reactive({
   nrow(datasetInput())
 })
-However, since this technique requires server-side calculation (which could take a long time, depending on what other reactive functions are executing) we recommend that you avoid using `output` in your conditions unless absolutely necessary. +However, since this technique requires server-side calculation (which could take a long time, depending on what other reactive expressions are executing) we recommend that you avoid using `output` in your conditions unless absolutely necessary. -### Creating Controls On the Fly With reactiveUI +### Creating Controls On the Fly With renderUI *Note: This feature should be considered experimental. Let us know whether you find it useful.* -Sometimes it's just not enough to show and hide a fixed set of controls. Imagine prompting the user for a latitude/longitude, then allowing the user to select from a checklist of cities within a certain radius. In this case, you can use the `reactiveUI` function to dynamically create controls based on the user's input. +Sometimes it's just not enough to show and hide a fixed set of controls. Imagine prompting the user for a latitude/longitude, then allowing the user to select from a checklist of cities within a certain radius. In this case, you can use the `renderUI` expression to dynamically create controls based on the user's input. #### ui.R @@ -68,12 +68,12 @@ uiOutput("cityControls")
#### server.R
# Partial example
-output$cityControls <- reactiveUI(function() {
+output$cityControls <- renderUI({
   cities <- getNearestCities(input$lat, input$long)
   checkboxGroupInput("cities", "Choose Cities", cities)
 })
-`reactiveUI` works just like `reactivePlot`, `reactiveText`, and the other reactive output functions you've seen before, but it expects the function it wraps to return an HTML tag (or a list of HTML tags, using `tagList`). These tags can include inputs and outputs. +`renderUI` works just like `renderPlot`, `renderText`, and the other output rendering functions you've seen before, but it expects the expression it wraps to return an HTML tag (or a list of HTML tags, using `tagList`). These tags can include inputs and outputs. In `ui.R`, use a `uiOutput` to tell Shiny where these controls should be rendered. diff --git a/_includes/tutorial/hello-shiny.md b/_includes/tutorial/hello-shiny.md index 5f0e00593..bc898c35b 100644 --- a/_includes/tutorial/hello-shiny.md +++ b/_includes/tutorial/hello-shiny.md @@ -9,7 +9,7 @@ The Hello Shiny example is a simple application that generates a random distribu Shiny applications have two components: a user-interface definition and a server script. The source code for both of these components is listed below. -In subsequent sections of the tutorial we'll break down all of the code in detail and explain the use of "reactive" functions for generating output. For now, though, just try playing with the sample application and reviewing the source code to get an initial feel for things. Be sure to read the comments carefully. +In subsequent sections of the tutorial we'll break down all of the code in detail and explain the use of "reactive" expressions for generating output. For now, though, just try playing with the sample application and reviewing the source code to get an initial feel for things. Be sure to read the comments carefully. The user interface is defined in a source file named ui.R: @@ -39,7 +39,7 @@ shinyUI(pageWithSidebar( )) -The server-side of the application is shown below. At one level, it's very simple--a random distribution with the requested number of observations is generated, and then plotted as a historgram. However, you'll also notice that the function which returns the plot is wrapped in a call to `reactivePlot`. The comment above the function explains a bit about this, but if you find it confusing, don't worry--we'll cover this concept in much more detail soon. +The server-side of the application is shown below. At one level, it's very simple--a random distribution with the requested number of observations is generated, and then plotted as a historgram. However, you'll also notice that the function which returns the plot is wrapped in a call to `renderPlot`. The comment above the function explains a bit about this, but if you find it confusing, don't worry--we'll cover this concept in much more detail soon. #### server.R @@ -48,14 +48,14 @@ The server-side of the application is shown below. At one level, it's very simpl # Define server logic required to generate and plot a random distribution shinyServer(function(input, output) { - # Function that generates a plot of the distribution. The function - # is wrapped in a call to reactivePlot to indicate that: + # Expression that generates a plot of the distribution. The expression + # is wrapped in a call to renderPlot to indicate that: # # 1) It is "reactive" and therefore should be automatically # re-executed when inputs change # 2) Its output type is a plot # - output$distPlot <- reactivePlot(function() { + output$distPlot <- renderPlot({ # generate an rnorm distribution and plot it dist <- rnorm(input$obs) diff --git a/_includes/tutorial/html-ui.md b/_includes/tutorial/html-ui.md index ebb17e01d..e4bfcd103 100644 --- a/_includes/tutorial/html-ui.md +++ b/_includes/tutorial/html-ui.md @@ -77,10 +77,10 @@ All of the changes from the original Tabsets application were to the user-interf # Define server logic for random distribution application shinyServer(function(input, output) { - # Reactive function to generate the requested distribution. This is - # called whenever the inputs change. The output functions defined - # below then all used the value computed from this function - data <- reactive(function() { + # Reactive expression to generate the requested distribution. This is + # called whenever the inputs change. The output renderers defined + # below then all used the value computed from this expression + data <- reactive({ dist <- switch(input$dist, norm = rnorm, unif = runif, @@ -93,9 +93,9 @@ shinyServer(function(input, output) { # Generate a plot of the data. Also uses the inputs to build the # plot label. Note that the dependencies on both the inputs and - # the data reactive function are both tracked, and all functions + # the data reactive expression are both tracked, and all expressions # are called in the sequence implied by the dependency graph - output$plot <- reactivePlot(function() { + output$plot <- renderPlot({ dist <- input$dist n <- input$n @@ -104,12 +104,12 @@ shinyServer(function(input, output) { }) # Generate a summary of the data - output$summary <- reactivePrint(function() { + output$summary <- renderPrint({ summary(data()) }) # Generate an HTML table view of the data - output$table <- reactiveTable(function() { + output$table <- renderTable({ data.frame(x=data()) }) }) diff --git a/_includes/tutorial/inputs-and-outputs.md b/_includes/tutorial/inputs-and-outputs.md index e6e75cb69..fdb7dad28 100644 --- a/_includes/tutorial/inputs-and-outputs.md +++ b/_includes/tutorial/inputs-and-outputs.md @@ -43,9 +43,9 @@ If you run the application again after making these changes you'll see the two u Next we need to define the server-side of the application which will accept inputs and compute outputs. Our server.R file is shown below, and illustrates some important concepts: * Accessing input using slots on the `input` object and generating output by assigning to slots on the `output` object. * Initializing data at startup that can be accessed throughout the lifetime of the application. -* Using a reactive function to compute a value shared by more than one output. +* Using a reactive expression to compute a value shared by more than one output. -The basic task of a Shiny server script is to define the relationship between inputs and outputs. Our script does this by accessing inputs to perform computations and by assigning reactive functions to output slots. +The basic task of a Shiny server script is to define the relationship between inputs and outputs. Our script does this by accessing inputs to perform computations and by assigning reactive expressions to output slots. Here is the source code for the full server script (the inline comments explain the implementation technqiues in more detail): @@ -63,20 +63,20 @@ mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manu # Define server logic required to plot various variables against mpg shinyServer(function(input, output) { - # Compute the forumla text in a reactive function since it is - # shared by the output$caption and output$mpgPlot functions - formulaText <- reactive(function() { + # Compute the forumla text in a reactive expression since it is + # shared by the output$caption and output$mpgPlot expressions + formulaText <- reactive({ paste("mpg ~", input$variable) }) # Return the formula text for printing as a caption - output$caption <- reactiveText(function() { + output$caption <- renderText({ formulaText() }) # Generate a plot of the requested variable against mpg and only # include outliers if requested - output$mpgPlot <- reactivePlot(function() { + output$mpgPlot <- renderPlot({ boxplot(as.formula(formulaText()), data = mpgData, outline = input$outliers) @@ -84,7 +84,7 @@ shinyServer(function(input, output) { }) -The use of `reactiveText` and `reactivePlot` to generate output (rather than just assigning values directly) is what makes the application reactive. These reactive wrappers return special functions that are only re-executed when their dependencies change. This behavior is what enables Shiny to automatically update output whenever input changes. +The use of `renderText` and `renderPlot` to generate output (rather than just assigning values directly) is what makes the application reactive. These reactive wrappers return special expressions that are only re-executed when their dependencies change. This behavior is what enables Shiny to automatically update output whenever input changes. ### Displaying Outputs diff --git a/_includes/tutorial/more-widgets.md b/_includes/tutorial/more-widgets.md index ac049844e..0adc8dbc6 100644 --- a/_includes/tutorial/more-widgets.md +++ b/_includes/tutorial/more-widgets.md @@ -74,7 +74,7 @@ library(datasets) shinyServer(function(input, output) { # Return the requested dataset - datasetInput <- reactive(function() { + datasetInput <- reactive({ switch(input$dataset, "rock" = rock, "pressure" = pressure, @@ -82,13 +82,13 @@ shinyServer(function(input, output) { }) # Generate a summary of the dataset - output$summary <- reactivePrint(function() { + output$summary <- renderPrint({ dataset <- datasetInput() summary(dataset) }) # Show the first "n" observations - output$view <- reactiveTable(function() { + output$view <- renderTable({ head(datasetInput(), n = input$obs) }) }) diff --git a/_includes/tutorial/reactivity.md b/_includes/tutorial/reactivity.md index 2e6d0798a..ade9ecce2 100644 --- a/_includes/tutorial/reactivity.md +++ b/_includes/tutorial/reactivity.md @@ -8,7 +8,7 @@ The Reactivity application is very similar to Hello Text, but goes into much mor > runExample("03_reactivity") -The previous examples have given you a good idea of what the code for Shiny applications looks like. We've explained a bit about reactivity, but mostly glossed over the details. In this section, we'll explore these concepts more deeply. +The previous examples have given you a good idea of what the code for Shiny applications looks like. We've explained a bit about reactivity, but mostly glossed over the details. In this section, we'll explore these concepts more deeply. If you want to dive in and learn about the details, see the Understanding Reactivity section, starting with [Reactivity Overview](#reactivity-overview). ### What is Reactivity? @@ -22,15 +22,15 @@ Shiny comes with a **reactive programming** library that you will use to structu ### Reactive Programming Basics -Reactive programming is a coding style that starts with **reactive values**--values that change over time, or in response to the user--and builds on top of them with **reactive functions**--functions that access reactive values and execute other reactive functions. +Reactive programming is a coding style that starts with **reactive values**--values that change over time, or in response to the user--and builds on top of them with **reactive expressions**--expressions that access reactive values and execute other reactive expressions. -What's interesting about reactive functions is that whenever they execute, they automatically keep track of what reactive values they read and what reactive functions they called. If those "dependencies" become out of date, then they know that their own return value has also become out of date. Because of this dependency tracking, changing a reactive value will automatically instruct all reactive functions that directly or indirectly depended on that value to re-execute. +What's interesting about reactive expressions is that whenever they execute, they automatically keep track of what reactive values they read and what reactive expressions they invoked. If those "dependencies" become out of date, then they know that their own return value has also become out of date. Because of this dependency tracking, changing a reactive value will automatically instruct all reactive expressions that directly or indirectly depended on that value to re-execute. -The most common way you'll encounter reactive values in Shiny is using the `input` object. The `input` object, which is passed to your `shinyServer` function, lets you access the web page's user input fields using a list-like syntax. Code-wise, it looks like you're grabbing a value from a list or data frame, but you're actually reading a reactive value. No need to write code to monitor when inputs change--just write reactive functions that read the inputs they need, and let Shiny take care of knowing when to call them. +The most common way you'll encounter reactive values in Shiny is using the `input` object. The `input` object, which is passed to your `shinyServer` function, lets you access the web page's user input fields using a list-like syntax. Code-wise, it looks like you're grabbing a value from a list or data frame, but you're actually reading a reactive value. No need to write code to monitor when inputs change--just write reactive expression that read the inputs they need, and let Shiny take care of knowing when to call them. -It's simple to create reactive functions: just pass a normal function definition into `reactive`. In this application, an example of that is the function that returns an R data frame based on the selection the user made in the input form: +It's simple to create reactive expression: just pass a normal expression into `reactive`. In this application, an example of that is the expression that returns an R data frame based on the selection the user made in the input form: -
datasetInput <- reactive(function() {
+
datasetInput <- reactive({
    switch(input$dataset,
           "rock" = rock,
           "pressure" = pressure,
@@ -38,14 +38,14 @@ It's simple to create reactive functions: just pass a normal function definition
 })
 
-To turn reactive values into outputs that can viewed on the web page, we assigned them to the `output` object (also passed to the `shinyServer` function). Here is an example of an assignment to an output that depends on both the `datasetInput` reactive function we just defined as well as `input$obs`: +To turn reactive values into outputs that can viewed on the web page, we assigned them to the `output` object (also passed to the `shinyServer` function). Here is an example of an assignment to an output that depends on both the `datasetInput` reactive expression we just defined, as well as `input$obs`: -
output$view <- reactiveTable(function() {
+
output$view <- renderTable({
    head(datasetInput(), n = input$obs)
 })
 
-This function will be re-executed (and its output re-rendered in the browser) whenever either the `datasetInput` or `input$obs` value changes. +This expression will be re-executed (and its output re-rendered in the browser) whenever either the `datasetInput` or `input$obs` value changes. ### Back to the Code @@ -89,7 +89,7 @@ shinyUI(pageWithSidebar( ### Server Script -The server script declares the `datasetInput` reactive function as well as three reactive output values. There are detailed comments for each definition that describe how it works within the reactive system: +The server script declares the `datasetInput` reactive expression as well as three reactive output values. There are detailed comments for each definition that describe how it works within the reactive system: #### server.R @@ -99,47 +99,47 @@ library(datasets) # Define server logic required to summarize and view the selected dataset shinyServer(function(input, output) { - # By declaring datasetInput as a reactive function we ensure that: + # By declaring datasetInput as a reactive expression we ensure that: # # 1) It is only called when the inputs it depends on changes # 2) The computation and result are shared by all the callers (it # only executes a single time) - # 3) When the inputs change and the function is re-executed, the + # 3) When the inputs change and the expression is re-executed, the # new result is compared to the previous result; if the two are # identical, then the callers are not notified # - datasetInput <- reactive(function() { + datasetInput <- reactive({ switch(input$dataset, "rock" = rock, "pressure" = pressure, "cars" = cars) }) - # The output$caption is computed based on a reactive function that + # The output$caption is computed based on a reactive expression that # returns input$caption. When the user changes the "caption" field: # - # 1) This function is automatically called to recompute the output + # 1) This expression is automatically called to recompute the output # 2) The new caption is pushed back to the browser for re-display # - # Note that because the data-oriented reactive functions below don't - # depend on input$caption, those functions are NOT called when + # Note that because the data-oriented reactive expression below don't + # depend on input$caption, those expression are NOT called when # input$caption changes. - output$caption <- reactiveText(function() { + output$caption <- renderText({ input$caption }) - # The output$summary depends on the datasetInput reactive function, + # The output$summary depends on the datasetInput reactive expression, # so will be re-executed whenever datasetInput is re-executed # (i.e. whenever the input$dataset changes) - output$summary <- reactivePrint(function() { + output$summary <- renderPrint({ dataset <- datasetInput() summary(dataset) }) - # The output$view depends on both the databaseInput reactive function + # The output$view depends on both the databaseInput reactive expression # and input$obs, so will be re-executed whenever input$dataset or # input$obs is changed. - output$view <- reactiveTable(function() { + output$view <- renderTable({ head(datasetInput(), n = input$obs) }) }) diff --git a/_includes/tutorial/scoping.md b/_includes/tutorial/scoping.md index fb09bc3df..7e1cbf3c8 100644 --- a/_includes/tutorial/scoping.md +++ b/_includes/tutorial/scoping.md @@ -106,7 +106,7 @@ shinyServer(function(input, output) { # Objects in this file are defined in each session source('each_session.R', local=TRUE) - output$text <- reactiveText(function() { + output$text <- renderText({ # Objects in this file are defined each time this function is called source('each_call.R', local=TRUE) diff --git a/_includes/tutorial/shiny-text.md b/_includes/tutorial/shiny-text.md index 1d28cbb4e..ed686136c 100644 --- a/_includes/tutorial/shiny-text.md +++ b/_includes/tutorial/shiny-text.md @@ -44,10 +44,10 @@ shinyUI(pageWithSidebar( The server side of the application has also gotten a bit more complicated. Now we create: -* A reactive function to return the dataset corresponding to the user choice -* Two other reactive functions (`reactivePrint` and `reactiveTable`) that return the output$summary and output$view values +* A reactive expression to return the dataset corresponding to the user choice +* Two other rendering expressions (`renderPrint` and `renderTable`) that return the `output$summary` and `output$view` values -These reactive functions work similarly to the `reactivePlot` function used in the first example: by declaring a reactive function you tell Shiny that it should only be executed when its dependencies change. In this case that's either one of the user input values (input$dataset or input$n) +These expressions work similarly to the `renderPlot` expression used in the first example: by declaring a rendering expression you tell Shiny that it should only be executed when its dependencies change. In this case that's either one of the user input values (`input$dataset` or `input$n`). #### server.R @@ -58,7 +58,7 @@ library(datasets) shinyServer(function(input, output) { # Return the requested dataset - datasetInput <- reactive(function() { + datasetInput <- reactive({ switch(input$dataset, "rock" = rock, "pressure" = pressure, @@ -66,16 +66,16 @@ shinyServer(function(input, output) { }) # Generate a summary of the dataset - output$summary <- reactivePrint(function() { + output$summary <- renderPrint({ dataset <- datasetInput() summary(dataset) }) # Show the first "n" observations - output$view <- reactiveTable(function() { + output$view <- renderTable({ head(datasetInput(), n = input$obs) }) })
-We're introduced more use of reactive functions but haven't really explained how they work yet. The next example will start with this one as a baseline and expand significantly on how reactive functions work in Shiny. +We've introduced more use of reactive expressions but haven't really explained how they work yet. The next example will start with this one as a baseline and expand significantly on how reactive expressions work in Shiny. diff --git a/_includes/tutorial/sliders.md b/_includes/tutorial/sliders.md index 6e041ae85..7f1ea8435 100644 --- a/_includes/tutorial/sliders.md +++ b/_includes/tutorial/sliders.md @@ -69,8 +69,8 @@ The server side of the Slider application is very straightforward: it creates a # Define server logic for slider examples shinyServer(function(input, output) { - # Reactive function to compose a data frame containing all of the values - sliderValues <- reactive(function() { + # Reactive expression to compose a data frame containing all of the values + sliderValues <- reactive({ # Compose data frame data.frame( @@ -88,7 +88,7 @@ shinyServer(function(input, output) { }) # Show the values using an HTML table - output$values <- reactiveTable(function() { + output$values <- renderTable({ sliderValues() }) }) diff --git a/_includes/tutorial/tabsets.md b/_includes/tutorial/tabsets.md index 28d2f320c..310824109 100644 --- a/_includes/tutorial/tabsets.md +++ b/_includes/tutorial/tabsets.md @@ -55,7 +55,7 @@ shinyUI(pageWithSidebar( ### Tabs and Reactive Data -Introducing tabs into our user-interface underlines the importance of creating reactive functions for shared data. In this example each tab provides its own view of the dataset. If the dataset is expensive to compute then our user-interface might be quite slow to render. The server script below demonstrates how to calculate the data once in a reactive function and have the result be shared by all of the output tabs: +Introducing tabs into our user-interface underlines the importance of creating reactive expressions for shared data. In this example each tab provides its own view of the dataset. If the dataset is expensive to compute then our user-interface might be quite slow to render. The server script below demonstrates how to calculate the data once in a reactive expression and have the result be shared by all of the output tabs: #### server.R @@ -64,10 +64,10 @@ Introducing tabs into our user-interface underlines the importance of creating r # Define server logic for random distribution application shinyServer(function(input, output) { - # Reactive function to generate the requested distribution. This is - # called whenever the inputs change. The output functions defined - # below then all use the value computed from this function - data <- reactive(function() { + # Reactive expression to generate the requested distribution. This is + # called whenever the inputs change. The renderers defined + # below then all use the value computed from this expression + data <- reactive({ dist <- switch(input$dist, norm = rnorm, unif = runif, @@ -80,9 +80,9 @@ shinyServer(function(input, output) { # Generate a plot of the data. Also uses the inputs to build the # plot label. Note that the dependencies on both the inputs and - # the 'data' reactive function are both tracked, and all functions + # the 'data' reactive expression are both tracked, and all expressions # are called in the sequence implied by the dependency graph - output$plot <- reactivePlot(function() { + output$plot <- renderPlot({ dist <- input$dist n <- input$n @@ -91,12 +91,12 @@ shinyServer(function(input, output) { }) # Generate a summary of the data - output$summary <- reactivePrint(function() { + output$summary <- renderPrint({ summary(data()) }) # Generate an HTML table view of the data - output$table <- reactiveTable(function() { + output$table <- renderTable({ data.frame(x=data()) }) })