-
Notifications
You must be signed in to change notification settings - Fork 41
/
10-dynamic_ui.rmd
372 lines (241 loc) · 10.9 KB
/
10-dynamic_ui.rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# Dynamic UI
**Learning objectives:**
Learn how to **add dynamics to a Shiny app** to be able to see interactive changes to potentially automate command functions for more complex visualizations.
At the end of this chapter, you will be able to understand what is a dynamic user interface, and what are the functions to update, hide or make visible and render the changes made interactively from UI to the server output.
## Introduction
This chapter based on Dynamic UI is made to addressing the user interface by updating the server outcome. There are three main sections, all of them relating to automated functions, able to dynamically change the output passing through parallel commands between the UI and the Server.
**What is a dynamic user interface?**
**How to create dynamic user interfaces?**
One way to do this is changing the UI using *code run* in the Server function, and by modifying inputs and outputs to see dynamic changes in the app.
## Updating inputs
**Three are the key techniques for creating dynamic user interface:**
```{r message=FALSE, warning=FALSE, include=FALSE}
library(shiny)
library(dplyr, warn.conflicts = FALSE)
library(DiagrammeR)
```
---------------------------------------
1 - update functions
2 - tabsetPanel()
3 - uiOutput() and renderUI()
-------------------------------------------
In this first part of the Dynamic UI chapter we will see how to pass from a basic structure to a more complicate one by adding dynamics to the output of the app, calling the "updating functions".
As we know the basic structure of a Shiny app is made of a **UI** (user interface) and a **Server**.
```{r echo=FALSE}
mermaid("
graph LR
A[User Interface]-->B[Server]
",height = '100%', width = '100%')
```
The first example is the User Interface and Server basic structure for updating the app.
```{r}
############################################
# ui <- fluidPage(
# [id]Input(),
# actionButton()
# )
#############################################
# server <- function(input, output, session) {
# observeEvent(input$... , {
# update[id]Input(inputId = ... , ... = input$... )
# })
# ...
# }
```
-----------------------------------------
### Update functions
```{r , echo=FALSE}
mermaid("
graph LR
A[id Input]-->B[update id Input]
",height = '100%', width = '100%')
```
The **Update function** allows you to modify the control after it has been created with a series of **[id]Input** and **update[id]Input**, as shown below:
```{r}
# [id]Input()
# textInput() # ui
# update [....] Input()
# updateTextInput() # server
# numericInput()
# update [....] Input()
# updateNumericInput()
#-------------------------------------
# selectInput()
# update [....] Input()
# updateSelectInput()
# sliderInput()
# update [....] Input()
# updateSliderInput()
```
**Hierarchical, Freezing and Circular references**
Other considerations need to be done when requesting the app to update following an interactive input request made by the user.
For example, the selection of *natural hierarchy* in the data is one of them, and it is important to create a user interface that allows updating the input maintaining stability while dynamically generating changes across multiple categories.
Further considerations involve establishing priorities with the application of key features such as *freezing Reactive Inputs*, a provided feature to freeze part of the inputs when expected a series of changes and so for establishing priorities and visually summarize data correctly.
```{r}
# [id]Input()
# tableOutput()
# observeEvent()
# update[id]Input()
```
The function **update[id]Input()** only affects all outputs and observers that have run, for this reason, the freezing function would let you hierarchically updating all your inputs before displaying it.
```{r}
# [id]Input()
# [some]Output()
# observeEvent()
# freezeReactiveValue() “freezing” the input
# update[id]Input()
```
The last consideration for this section is to *circularity* as seen in many apps, it is created when requested for making simultaneous changes recursively.
In other words, it is seen in apps when updating the input, automatically another input is created in the function of the first one. Under this condition, the cycle can create an recursive loop on the current value of the input bringing it to run again and again in circularity.
**How the "Action button" reset the input**
A simple example of the use of this command is the *Reset button*. It is one of the clearest examples of what is meant with making dynamic changes.
When the user interactively intervenes on the app making a choice, then the "reset" button makes it easy to reset the parameters back to their initial value.
```{r}
# [id]Input()
# actionButton()
# observeEvent()
# update[id]Input()
# [id]Input()
# actionButton()
# observeEvent()
# updateActionButton()
```
A simple use of the *reset input button* is shown in this example:
Spring temperature generally varies on average between 19 and 25 C° degrees, let's set an average value of 21 C° as *reset* point.
```{r}
ui <- fluidPage(
sliderInput("temperature", "Spring temperature", 21, min = 19, max = 25),
actionButton("reset", "Reset")
)
server <- function(input, output, session) {
observeEvent(input$reset,{
updateSliderInput(inputId = "temperature", value = 21)
})
}
```
-----------------------------------------------------
## Dynamic visibility
**To show and hide parts of the UI** dynamically and interactively.
-----------------------------------------------------------
The **tabsetPanel()** is the second function for this chapter and it involves the visibility of part of the app.
This second function is made to let the user show and/or hide some of the tabs set in the main panel. It is a technique that allows managing the appearance of the app with selecting visibility of the tabs as shown in the [Tabsets](https://shiny.rstudio.com/articles/layout-guide.html) section of the **Shiny.rstudio.com** guide's page of the website.
```{r echo=FALSE}
mermaid("
graph LR
A[tabsetPanel]-->B[updateTabsetPanel]
",height = '100%', width = '100%')
```
To enhance your **app** with features and the most wanted themes and as an example on how to set the visibility of a tab in your app, you can follow the steps found here: [Gallery](https://shiny.rstudio.com/gallery/tabsets.html)
As an example here is shown how to switch between panels hiding one panel and showing the other with different content.
```{r}
library(shiny)
################## switcher ##############################
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("controller", "Show", choices = c("plot","summary"))
),
mainPanel(
tabsetPanel(
id = "switcher",
type = "hidden",
tabPanelBody("plot", "Plot"),
tabPanelBody("summary", "Summary")
)
)
)
)
server <- function(input, output, session) {
observeEvent(input$controller, {
updateTabsetPanel(inputId = "switcher", selected = input$controller)
})
}
```
Finally, the technique of the *Conditional UI* allows you to simulate different parameters to be set in the app.
The **tabsetPanel()** is updated with input requests as a separate section with different types of **[id]Input()** and then embedded inside a fuller UI.
Example of a chunk of code to be integrated in the UI:
```{r}
parameter_tabs <- tabsetPanel(
tabPanel("normal",
numericInput("mean", "mean", value = 1)
)
)
```
```{r}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("n", "Number of samples", value = 100),
parameter_tabs # where to set the chunk of code
),
mainPanel(
plotOutput("hist")
)
)
)
```
There are other options to use to be able to switch among different pages, an example of this is creating a *wizard* and using the **switch_page()** function, this will be shown in [Chapter 18 - Functions](https://mastering-shiny.org/scaling-functions.html) as said in the book.
-----------------------------------------------------------------
## Creating UI with code
**Create and modify the user interface while the app is running**
-------------------------------------------------------------------
```{r , echo=FALSE}
mermaid("
graph LR
A[uiOutput]-->B[renderUI]
",height = '100%', width = '100%')
```
The last two functions **uiOutput()** and **renderUI()**, are for applying the technique of rendering the UI by setting the value of the new input to the current value of the existing control.
This technique gives the developer the ability to create and modify the user interface while the app is running.
**uiOutput()** act in the UI part of the app while **renderUI()** act in the Server.
In this contest the function **isolate()** would be able to do this isolating a particular input, for more info see: [Section 15.4.1](https://mastering-shiny.org/reactivity-objects.html#isolate)
Final example to show the position of the two functions inside the **UI** and the **Server**:
```{r}
##############################################
ui <- fluidPage(
selectInput("type", "type", c("slider", "numeric")),
uiOutput("numeric")
)
###############################################
server <- function(input, output, session) {
output$numeric <- renderUI({
if (input$type == "slider") {
sliderInput("n", "n", value = 0, min = 0, max = 100)
} else {
numericInput("n", "n", value = 0, min = 0, max = 100)
}
})
}
####################################
```
In addition to the aforementioned functions more features are available as a composition of each other and many more, to allow the user for *Multiple controls*, *Dynamic filtering* and *Dialog boxes*.
-------------------------------------------------------------------
## Conclusions
The dynamic of the UI can be appreciated by modification of the Server. Many compositions of the basic functions would let the user be able to interactively change the output to the desired visualization.
----------------------------------------------------------------------
## Meeting Videos
### Cohort 1
`r knitr::include_url("https://www.youtube.com/embed/U0gLzdGCEOs")`
### Cohort 2
`r knitr::include_url("https://www.youtube.com/embed/kW2-ybX39t8")`
`r knitr::include_url("https://www.youtube.com/embed/6-V8qZ_sT8w")`
<details>
<summary> Meeting chat log </summary>
```
01:03:01 Ryan Metcalf: Reference for <div> tags: https://www.w3schools.com/tags/tag_div.ASP#:~:text=The%20tag%20defines%20a,inside%20the%20tag!
01:14:09 Conor Tompkins: barplot(c(1:2), col = "red")
01:14:47 Conor Tompkins: barplot() automatically translates some strings to hex
```
</details>
### Cohort 3
`r knitr::include_url("https://www.youtube.com/embed/2LQyxFMJ4Kg")`
### Cohort 4
`r knitr::include_url("https://www.youtube.com/embed/5llm8FZB-es")`
### Cohort 5
`r knitr::include_url("https://www.youtube.com/embed/URL")`
<details>
<summary>Meeting chat log</summary>
```
LOG
```
</details>