```{r echo = FALSE} options(markdown.HTML.options = "toc") ``` # Fully Interactive Applications Static data can be made interactive using the [`static.analysis.page` function](StaticContent.html), and web services can be set up using the [`build.service` function](ExampleServers.html). This documentation page describes how to put these two systems together to build an application which can take user input, run a handler function on the R side, and return interactive data to the user. ## Rook >= 1.1 + fork Rook version >= 1.1 and fork are required for full functionality of the examples in this vignette. I am going to check if they are available. ```{r echo = FALSE, message=FALSE} library(AnalysisPageServer) rookforkOK <- AnalysisPageServer:::checkRookForkForVignettes() ``` ----------------- ## Two-color Maunga Whau The [last example from the Non-interactive Servers page](ExampleServers.html#toc_10) was a plot of an image in which the coloring was taken from a URL query parameter. This is how we built that service: ```{r eval=FALSE} volcano <- build.service(function(colors = c("red","white","purple")) { plotfile <- tempfile(fileext = ".png") png(plotfile, width = 600, height = 400) par(mar=c(1, 1, 4, 1)) col <- colorRampPalette(colors)(50) image(datasets::volcano, xaxt = "n", yaxt = "n", main = "Maunga Whau Volcano", col = col, cex.main = 2) dev.off() plot.data <- readBin(plotfile, "raw", n = file.info(plotfile)[, "size"]) new.response(body = plot.data, content.type = "image/png") }, name = "volcano") ``` The [last example from the Static Content page](StaticContent.html#toc_3) had the same plot with lots of front-end interactivity (rollover, zoom, filter, etc) but no possibility for the user to modify the results. *That* page was made like this: ```{r eval=TRUE} x <- rep(1:nrow(volcano), each = ncol(volcano)) y <- rep(1:ncol(volcano), nrow(volcano)) volcano.cells <- data.frame(x = x, y = y, Height = as.vector(t(volcano))) ``` ```{r eval = FALSE} plotfile3 <- tempfile(fileext = ".svg") svg(filename = plotfile3, width = 9, height = 7) image(volcano, xaxt = "n", yaxt = "n", main = "Maunga Whau Volcano") dev.off() result <- static.analysis.page(outdir = "static-example3", svg.files = plotfile3, dfs = volcano.cells, show.xy = TRUE, title = "Maunga Whau Volcano") ``` Using the complete AnalysisPageServer system we can build an application that not only combines these two features—customization via parameters and plot/data interactivity—but also features a user interface to select parameter values. In order to do this we'll use the `new.analysis.page` constructor instead of `build.service`, and instead of calling the `analysis` URL directly we'll open the application through the `analysis-page-server.html` landing page: ```{r message = FALSE} port <- 5333 library(AnalysisPageServer) volcano.handler <- function(color1 = "red", color2 = "blue") { par(mar=c(1, 1, 4, 1)) chosen.colors <- c(color1, color2) col <- colorRampPalette(chosen.colors)(50) image(datasets::volcano, xaxt = "n", yaxt = "n", main = "Maunga Whau Volcano", col = col, cex.main = 2) return(volcano.cells) } volcano.page <- new.analysis.page(handler = volcano.handler, name = "volcano", description = "Draw the Maunga Whau Volcano in Two Colors") reg <- new.registry(volcano.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` If you are running this example yourself you should register this handler now to prevent yourself from leaving running servers behind: ```{r eval = FALSE} on.exit(kill.process(srv)) ``` You can get the full URL to the landing page: ```{r} rook.analysis.page.server.landing.page(srv) ``` Now open that URL in your browser:

This is the default landing page. The main feature is an index of all the `AnalysisPage`s that we included in our Registry. Right now there is only one. Clicking on the "Tools" menu in the upper right opens a drop-down so you can jump to another tool. In this example there is only one page there:

Click on "volcano" (either on the main landing page or in the "Tools" menu) to go to its "primary parameter area".

Now we can see the two arguments of the handler function, `color1` and `color2`, have been rendered as HTML text inputs, and prepopulated with their default values. (This is default behavior, but there is a rich syntax to define complex parameters, and this is explored below.) Also note that the URL has updated. This new URL, with the suffix `#page/volcano/primary`, will automatically jump to the primary parameter area for the `volcano` page. Clicking on `Submit` takes us to the analysis result:

This page features the plot (top left), a control area (top right) and the data table (below, but not visible on this screen shot). The interactivites discussed on the Interactivity documentation page, including **roll-over, filtering, zooming, tagging and download**, are all available with this deployment. Since they are described in detail there no more will be said about them here. What's new is the "Analysis Parameters" section, which is a condensed version of the primary parmeter area and lets the user see the inputs to the function. This section can also be used to repeat the analysis with modified parameters. First, click on Modify:

Then input the new values and click "Change":

and, voilà, the Maunga Whau Volcano in cranberry and teal:

Let's turn off the server in preparation for the next example. ```{r} kill.process(srv) ``` ------------- ## Multiple tools in one Application If your registry has multiple `AnalysisPage`s then each one will become a different tool in your application. Let's add a boring page that has no parameters. It will plot the speeds and distances from the `cars` dataset. We have to duplicate the columns if we actually want to display the data because the `$x` and `$y` fields are always stripped. ```{r message=FALSE} cars.df <- cbind(x = cars$speed, y = cars$dist, cars) cars.page <- new.analysis.page(function() { plot(cars.df$x, cars.df$y, xlab = "Speed", ylab = "Stopping Distance", pch = 19, col = adjustcolor(1, alpha.f = 0.6)) cars.df }, name = "cars", description = "Speed and Stopping Distances of Cars") reg <- new.registry(cars.page, volcano.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` (No need to do the `on.exit(kill.process(srv))` again since the old handler is still registered.) Now the landing page has two tools on it.

If you roll over the name of the tool you can see a short description:

The `cars` page has an empty parameter area:

And the plot:

```{r} kill.process(srv) ``` ## Parameter Construction Each of the color parameters for the volcano example was rendered as a simple text input. Depending on the default values of your handler function, different types of widgets will be used. The functions `default.param.set` and `default.param` introspect your handler function and decide what type of widget to render. These are called automatically by the constructor for `AnalysisPage`s, `new.analysis.page`, if you don't provide a `param.set` argument explicitly. By using different default arguments you can build relatively complicated input widgets. For example, a vector of length greater than 1 becomes a drop-down menu. These default parameters are covenient but they are not very good engineering practices, because the default value is no longer an actual parameter value but instead a signal for widget type. In particular, if you don't provide `skip.checks = TRUE` to `new.analysis.page()` it will actually run your handler function once with the default values to make sure you've given it something reasonable. To specify your input widgets explicitly you'll build an `AnalysisPageParam` for each input variable, and then put them all together into an `AnalysisPageParamSet` using the constructor `param.set()`. These are the constructors for the "atomic" `AnalysisPageParam`s. These result in a single value being passed to the function. | constructor | Description | Type within `R` | | --- | --- | --- | | `simple.param` | Simple text input elements | Arbitrary Length-1 character vector | | `slider.param` | Numeric slider-type parameter with min, max and step-size | Length-1 numeric vector | | `select.param` | Select from a drop-down list or radio group | Length-1 character vector, but only from those defined in the drop-down choices | | `bool.param` | Toggle button | `TRUE` or `FALSE`, according to whether the button is pressed | | `combobox.param` | Combobox-type parameter with search box and dropdown | Length-1 character vector | The `combobox.param` takes the user input and calls back to the server to get a list of search hits. It requires including a service-type (non-interactive) page to serve these search hits (or otherwise providing that service from some other accessible URL). These constructors, which are variants of the atomic constructors, result in multiple selections: | constructor | `allow.multiple` parameter | Description | Type within `R` | | --- | --- | --- | --- | | `select.param` | `TRUE` | Select multiple values from a dropdown | Character vector of selected values. | | `combobox.param` | `TRUE` | Combobox-type parameter with search box and dropdown, and option to select multiple values | Character vector of selected values | These two constructors build more complex widgets that result in either arrays or lists: | constructor | Description | Type within `R` | | --- | --- | --- | | `array.param` | Expandable array of identical parameters | List or vector of similarly-typed values (the choice of list or vector is decided by `toJSON`, since there is no difference in the front-end) | | `compound.param` | Group of related parameters | The related parameters are packed into a list (or possibly a named vector) and sent to your function | Since the function parameter values are always built by `toJSON` you should plan to write things a bit defensively. The main thing to remember is that text-input numbers are passed as strings, so you have to call `as.numeric()` for a `simple` parameter (although this is not necessary for a `slider` param), and that, given a JSON array, `toJSON` will decide to build an R vector or list according to whether the objects have the same type. So don't assume you have either, and either cast your vectors to lists with `as.list` or avoid using the `$` operator, which doesn't work on vectors. The examples that follow attempt to demonstrate the main features of the `AnalysisPageParam` system. Reading the documentation of the individual constructors will contain more information for specific cases. For the first example we will re-use the same handler function from the volcano plot but have its input widgets be drop-downs: ```{r message=FALSE} color.choices <- c("red", "orange", "yellow", "green", "blue", "purple") color1 <- select.param(name = "color1", label = "Low Color", description = "Select a color for the lowest parts of the volcano", choices = color.choices) color2 <- select.param(name = "color2", label = "High Color", description = "Select a color for the highest parts of the volcano", choices = color.choices) color.pset <- param.set(color1, color2) volcano.page <- new.analysis.page(handler = volcano.handler, param.set = color.pset, name = "volcano", description = "Draw the Maunga Whau Volcano in Two Colors") reg <- new.registry(volcano.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` Now the parameters look like this:

Note that we've also had a chance to give the parameter a prettier display name ("Low Color" and "High Color") and a slightly longer description. This can really help to improve the user experience, and it doesn't require any modification to your handler function. Also, since we've isolated the construction of the parameter widgets, we can define our own constructors, and even re-use them between pages. For example, if our user has to choose between six colors in several different pages, we can define a color parameter constructor like this: ```{r} color.param <- function(name = "color", label = "Color", description = "Select a color from the list", ...) { select.param(name = name, label = label, description = description, choices = color.choices, ...) } ``` And then wherever we need a color selector we've got the same one: ```{r eval = FALSE} color1 <- color.param("color1", label = "Low Color") color2 <- color.param("color1", label = "High Color") ``` ```{r} kill.process(srv) ``` --------- ## Slider parameter (with HTML response) This example shows off the slider widget, and also shows how to have your handler return HTML instead of the plot/dataset combination. ```{r} slider <- slider.param(name = "n", min = 0, max = 100, step = 1, value = 50, description = "Select a number between 0 and 100") handler <- function(n=50) { ## n is already numeric for a slider---no need to cast with as.numeric(n) html <- paste0("

This is an HTML response

", "") ## This is how to have your page return arbitrary HTML, rather than ## the typical plot/dataset combination new.datanode.html("html", html) } slider.page <- new.analysis.page(handler, param.set = param.set(slider), annotate.plot = FALSE, annotate.data.frame = FALSE, no.plot = TRUE, label = "slider widget with HTML response") reg <- new.registry(slider.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` Here is the widget:

And here is the rendered HTML response:

```{r} kill.process(srv) ``` -------- ## Array parameter The volcano plot is really a lot nicer with more colors so that finer differences in color are revealed. We could add a third color like this: ```{r eval = FALSE} volcano.handler <- function(color1 = "red", color2 = "blue", color3 = "green") { par(mar=c(1, 1, 4, 1)) chosen.colors <- c(color1, color2, color3) col <- colorRampPalette(chosen.colors)(50) image(datasets::volcano, xaxt = "n", yaxt = "n", main = "Maunga Whau Volcano", col = col, cex.main = 2) return(volcano.cells) } ``` What if the user wants 4 or more colors? One solution is to use an array-type parameter. This is done with the constructor `array.param`. We pass a "prototype", in this case a single color picker, and the user can add as many color pickers to the array as she wishes. We'll require that the user have at least 2 colors, since a single color would be useless. ```{r} volcano.handler2 <- function(colors = c("red","blue")) { par(mar=c(1, 1, 4, 1)) col <- colorRampPalette(colors)(50) image(datasets::volcano, xaxt = "n", yaxt = "n", main = "Maunga Whau Volcano", col = col, cex.main = 2) return(volcano.cells) } one.color <- color.param() color.array <- array.param(name = "colors", label = "Colors", description = "Color the volcano from low to high altitude", prototype = one.color, min = 2, start = 2) ## Build an "AnalysisPageParamSet" with just 1 parameter, an array-type parameter array.pset <- param.set(color.array) volcano.page <- new.analysis.page(handler = volcano.handler2, param.set = array.pset, name = "volcano", description = "The Maunga Whau Volcano in Many Colors") reg <- new.registry(volcano.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` Now the parameter area starts like this:

(If you are trying to follow along yourself and you still see the 2-parameter example instead of the array then you may need to clear some cache, or directly refresh the parameter resource [http://127.0.0.1:5333/custom/RAPS/R/params?page=volcano](http://127.0.0.1:5333/custom/RAPS/R/params?page=volcano).) The `-` and `+` buttons remove and add single color widgets respectively. Note that the `-` button starts disabled since we said the minimum number of colors would be 2. Click on the `+` to get more color widgets:

And so on:

and submit

```{r} kill.process(srv) ``` ## Multiple selection parameter Another way to allow for multiple selections is with the `allow.multiple` parameter of the `select.param` constructor: ```{r} multi.color.selector <- select.param(name = "color", label = "Color", description = "Select multiple colors", choices = color.choices, allow.multiple = TRUE) volcano.page <- new.analysis.page(handler = volcano.handler2, param.set = param.set(multi.color.selector), name = "volcano", description = "The Maunga Whau Volcano in Many Colors") reg <- new.registry(volcano.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` Now the parameter choices render as a multiple-selection dropdown:

The user can select as many of the choices from the menu. As they are chosen the accumulate in the area above. "Select All" to select the remaining choices, and "Deselect All" to clear the selection. After selecting, the order of the choices can be changed with drag-and-drop, and individual choices can be removed by clicking the small `x`. ```{r} kill.process(srv) ``` ---------- ## Comboboxes A *combobox* is an input widget that has both a text entry area and then, depending on what is typed there, a dropdown. In order to have a combobox, we'll first need to set up a service to return the search hits. This can be served from outside your `AnalysisPage` application if you can avoid cross-domain issues. This example will include the service in the same application. Let's have the user choose a city in a state. First we'll choose the states from a drop-down, then choose a city. This sets up some data structures on which we will compute: ```{r} cities <- list(IL = c("Deerfield", "Springfield", "Chicago", "Urbana"), CA = c("San Francisco", "San Diego", "Los Angeles", "Sacramento"), MA = c("Deerfield", "Springfield", "Boston", "Camridge", "Arlington")) states <- names(cities) ``` The `cities` service will return cities in a state: ```{r} cities.service <- build.service(function(state) cities[[state]], name = "cities") ``` ```{r} state.dropdown <- select.param(name = "state", label = "State", description = "Choose a state", choices = states) city.cbx <- combobox.param(name = "city", label = "City", description = "Choose a city", uri = '/custom/RAPS/R/analysis?page="cities"&state=":statename"', dependent.params = c(statename="state")) ``` The ":" indicates a token within the URI which will be substituted with the value of another input widget. The `dependent.params` gives the mapping from tokens to parameter names. Here we'll replace `:statename` with the value of the "state" dropdown. ```{r} handler <- function(state = "MA", city = "Arlington") { plot.new() plot.window(0:1, 0:1) mesg <- paste(sep = "", city, ", ", state, " is for lovers\nand data analysts.") text(0.5, 0.5, mesg, cex = 3) data.frame(x = numeric(), y = numeric()) } page <- new.analysis.page(handler = handler, param.set(state.dropdown, city.cbx), name = "state", description = "Information about a city", annotate.plot = FALSE) reg <- new.registry(cities.service, page) srv <- startRookAnalysisPageServer(reg, port = port) ``` The primary parameter area starts like this:

The cities available are only those in Illinois. If the user chooses a different state from the first dropdown then the second dropdown is populated with different city names:

```{r} kill.process(srv) ``` Purists might argue that while having conditionally populated dropdown menus is useful they are not true comboboxes, which are really a text/dropdown mixed input widget. In `AnalysisPageServer` that is called a *self-dependent* combobox. The constructor is exactly the same, just that the widget itself is a dependent parameter, and so whatever text is typed in gets sent. To demonstrate, we'll create a city searching service which only returns cities within the state matching a query. ```{r} ## Extra "as.list" is necessary to force creation of JSON array for length 1 city.search.service <- build.service(function(state, query) as.list(grep(query, cities[[state]], value = TRUE)), name = "city_search") city.cbx <- combobox.param(name = "city", label = "City", description = "Enter a search term to find a city", uri = "/custom/RAPS/R/analysis?page=\"city_search\"&state=\":statename\"&query=\":query\"", dependent.params = c(statename="state", query = "city")) ``` Note that "city" is now dependent on itself. That will make it a true combobox. We can now rebuild the `AnalysisPage` with a different `AnalysisPageParameterSet`. ```{r} page <- new.analysis.page(handler = handler, param.set(state.dropdown, city.cbx), name = "state", description = "Information about a city", annotate.plot = FALSE) reg <- new.registry(city.search.service, page) srv <- startRookAnalysisPageServer(reg, port = port) ``` Now the widget for "city" renders as a combobox instead of a dropdown.

After you type in a search term the options render below:

Click on one and the text box collapses to your suggestion:

> The combobox feature is relatively unstable under Rook, possibly due to the quick successive AJAX calls triggered by keystrokes. You may need to restart your server frequently. However, it behaves well under [RApache](ApacheDeployment.html) and [FastRWeb](FastRWebDeployment.html). ```{r} kill.process(srv) ``` ----------- ## Persistent parameters Until now all of the examples either had a single page or two completely independent pages, between which no data passes. `AnalysisPageServer` has a very limited but still useful mechanism to share information between the different pages in an application: *persistent parameters*. Each application has a persistent parameter space, which is simply a hash-key lookup. This is maintained strictly on the client side. What the server can do is couple certain parameters to particular keys in this hash. Then all of the parameters tagged to a particular hash key are now coupled---changing one on any page changes them on all the pages. Persistent parameters could be used for example to select a data set, then have all of the pages analyze the same data set. Or they could be used for some finer customizations. For example, if each page were a different volcano, you might want to have the color array be persistent between the pages. Let's work up the color example. We'll make an application that has two pages. The key to this whole thing working is to set the argument `persistent` in whichever parameter you want to be persistent. In this example we'll have two pages that each take a single parameter called "word". ```{r} horiz.handler <- function(word = "word") { plot.new() plot.window(0:1, 0:1) text(0.5, 0.5, word) data.frame(x = numeric(), y = numeric()) } vert.handler <- function(word = "word") { plot.new() plot.window(0:1, 0:1) text(0.5, 0.5, word, srt = 90) data.frame(x = numeric(), y = numeric()) } ``` Next, the "word" parameter on both pages is coupled to the persistent "word" parameter. ```{r} word <- simple.param(name = "word", persistent = "word") horiz.page <- new.analysis.page(horiz.handler, param.set(word), name = "horiz", label = "Horizontal") vert.page <- new.analysis.page(vert.handler, param.set(word), name = "vert", label = "Vertical") reg <- new.registry(horiz.page, vert.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` Here is the primary parameter area for the first page. We'll type in "foo" as the input word:

The "foo" value does not persist until the user clicks "Submit". After that, when the user next goes to the other tool, "word" will start already with the value "foo":

```{r} kill.process(srv) ``` ### Conditionally Persistent Parameters Persistent parameters can remember a single value. Sometimes richer dependencies exist. For example, imagine that the user should first select a language then type in a word in that language. We can make both the language and word persistent between the pages using the `persistent` argument. However, if the user switches, say, from French back to English, then the French word would persist. If we want the word for each language to persist *independently* then we can use *conditionally persistent parameters*. To do this, use the `persistent.dependencies` argument to your Parameter constructor: ```{r conditional-persistent-params} ## First we'll rewrite the two handlers to accept ## a "language" parameter, and display "language: word" ## instead of just "word" horiz.handler <- function(language = "English", word = "word") { plot.new() plot.window(0:1, 0:1) text(0.5, 0.5, paste0(language, ": ", word)) data.frame(x = numeric(), y = numeric()) } vert.handler <- function(language = "English", word = "word") { plot.new() plot.window(0:1, 0:1) text(0.5, 0.5, paste0(language, ": ", word), srt = 90) data.frame(x = numeric(), y = numeric()) } ## Now define language as a dropdown parameter, and make it ## persistent between the two pages. language <- select.param(name = "language", value = "English", choices = c("English","Wolof","Chinese","Arabic","Klingon"), persistent = "language") ## Then define the word parameter the same as above ## but make its persistence dependent on language. word <- simple.param(name = "word", persistent = "word", persistent.dependencies = "language") ## Finally, build the App and deploy: horiz.page <- new.analysis.page(horiz.handler, param.set(language, word), name = "horiz", label = "Horizontal") vert.page <- new.analysis.page(vert.handler, param.set(language, word), name = "vert", label = "Vertical") reg <- new.registry(horiz.page, vert.page) srv <- startRookAnalysisPageServer(reg, port = port) ``` Let's see how it looks. First we'll start on the `Horizontal` page, select `English` language and type in the word `Hello`:

The result:

Next we'll modify the analysis parameters. Let's switch the language to Wolof,

and make a plot with a Wolof word:

That's fine. Up until now we haven't demonstrated anything persistent, much less conditionally persistent. But inside the browser's cache it now remembers the English word "Hello" and the Wolof word "Merhbe", both for this page and for the `Vertical` page. Let's switch to the `Vertical` page:

The form is initialized with the last language (Wolof) and word ("Merhbe"). This is the normal behavior for regular persistent parameters. Now, to demonstrate the *conditional* persistence, let's switch to English:

Once English is selected the `word` field automatically populates with the last English word, "Hello":

If we switch to a new language then the `word` field is simply reset:

```{r echo = FALSE} kill.process(srv) ``` --------------- ## Advanced/hidden parameters Any parameter can be made advanced by providing `advanced = 1` to its constructor. This means that by default it will not show up unless the user exposes it by clicking the "advanced" icon. Here is a very simple page which plots the sine function. The range will be from 0 to `xmax`, where the user can type in `xmax`. The number of points used will default to 100, and the user will be allowed to change that, but it will be a hidden, advanced parameter. ```{r} xmax <- simple.param("xmax", label = "Maximum Theta", value = pi) n <- simple.param("n", value = 100, advanced = 1) pset <- param.set(xmax, n) handler <- function(xmax = pi, n = 100) { xmax <- as.numeric(xmax) n <- as.numeric(n) x <- seq(0, xmax, length = n) y <- sin(x) plot(x, y, pch = 19, xlab = "Theta", ylab = "Sine(Theta)", main = "Sine curve") data.frame(x = x, y = y, Theta = x, `Sin(theta)` = y, check.names = FALSE) } page <- new.analysis.page(handler, param.set = pset, name = "sine", label = "Sine Plotter") reg <- new.registry(page) srv <- startRookAnalysisPageServer(reg, port = port) ```

Note the appearance of the advanced icon next to the Cancel and Submit buttons. This indicates that there are hidden advanced parameters.

Rolling over the icon lists which parameters are missing, and clicking on it exposes them:

> Note: When an analysis is submitted and advanced mode is *off*, the advanced parameter(s) are simply omitted from the function call. Typically this means that the function would use the default value for those parameters from the function definition. This does not necessarily match the default value for that parameter in the front end. ```{r echo = FALSE} kill.process(srv) ``` ---------------- ## Conditional Parameters with "show.if" Aside from making it advanced, the visibility of a parameter can also be controlled by making it conditional on some other parameter. Let's continue the example of the sine curve from the previous section. Let's suppose we want the user to be allowed to specify either `xmin` or `xmax`. (We'll leave the widget for choosing `n` out of this example.) We need three widgets---one to choose either `xmin` or `xmax`, and then one for each of `xmin` and `xmax`. And we need to "program" the `xmin` and `xmax` to only display depending on the value of the first. This is done with the `show.if` argument to the `AnalysisPageParam` constructor. ```{r} extremum.chooser <- select.param("extremum", choices = c("xmin", "xmax")) xmin <- simple.param("xmin", value = 0, show.if = list(name = "extremum", values = "xmin"), label = "Minimum Theta", description = "Set a minimum value for the range (max will be pi)") xmax <- simple.param("xmax", value = pi, show.if = list(name = "extremum", values = "xmax"), label = "Maximum Theta", description = "Set a maximum value for the range (min will be 0)") pset <- param.set(extremum.chooser, xmin, xmax) handler <- function(extremum = "xmin", xmin = 0, xmax = pi) { xmin <- if(extremum == "xmin") as.numeric(xmin) else 0 xmax <- if(extremum == "xmax") as.numeric(xmax) else pi x <- seq(xmin, xmax, length = 200) y <- sin(x) plot(x, y, pch = 19, xlab = "Theta", ylab = "Sine(Theta)", main = "Sine curve") data.frame(x = x, y = y, Theta = x, `Sin(theta)` = y, check.names = FALSE) } page <- new.analysis.page(handler, param.set = pset, name = "sine_with_extrema", label = "Sine Plotter") reg <- new.registry(page) srv <- startRookAnalysisPageServer(reg, port = port) ``` This page's parameter area starts out looking like this:

If the user selects "xmax" for the extremum then the second widget changes:

Really two things happen---the `xmin` widget disappears and the `xmax` widget appears. ```{r echo = FALSE} kill.process(srv) ``` ---------------- ## Compound Parameters Sometimes a finite set of parameters are logically linked. For example, in the previous section we had the 3 widgets to select an extremum. If we added back in a widget to choose how many points, then it is somehow logically distinct from the other three. It would be nice both from the user interface point of view and also from the engineering point of view to reflect this. Let's put those three in a single "compound"-type parameter. Then they will be visually separated from the fourth parameter for the user, and also provided to the handler packed into a single list. The constructor `compound.param` is expecting an `AnalysisPageParamSet`, the same object that we can pass to `new.analysis.page`. ```{r} range.paramset <- param.set(extremum.chooser, xmin, xmax) range <- compound.param("range", children = range.paramset) n <- simple.param("n", value = 100) pset <- param.set(range, n) handler <- function(range = list(extremum = "xmin", xmin = 0), n = 100) { xmin <- if(range$extremum == "xmin") as.numeric(range$xmin) else 0 xmax <- if(range$extremum == "xmax") as.numeric(range$xmax) else pi x <- seq(xmin, xmax, length = as.numeric(n)) y <- sin(x) plot(x, y, pch = 19, xlab = "Theta", ylab = "Sine(Theta)", main = "Sine curve") data.frame(x = x, y = y, Theta = x, `Sin(theta)` = y, check.names = FALSE) } page <- new.analysis.page(handler, param.set = pset, name = "sine_with_extrema", label = "Sine Plotter") reg <- new.registry(page) srv <- startRookAnalysisPageServer(reg, port = port) ``` The parameter area now looks like this:

Combining `show.if`, `array.param` and `compound.param` one can thus create arbitrarily nested and complex parameters. For example, the `prototype` for an `array.param` could be a `compound.param`. Then each time the user clicks on `+` a extra row of parameters would appear. This could be used to input first and last names for a list of people. ```{r} kill.process(srv) ``` # Next [Deployment with Apache](ApacheDeployment.html)