Skip to content

Commit f91f487

Browse files
committed
Update README.md
1 parent b16a3cf commit f91f487

File tree

2 files changed

+107
-76
lines changed

2 files changed

+107
-76
lines changed

DESCRIPTION

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Package: shinyEvents
22
Type: Package
33
Title: Shiny wrapper with event handlers instead of reactivity
4-
Version: 2.2
5-
Date: 2017-05-23
4+
Version: 2.3
5+
Date: 2018-02-28
66
Author: Sebastian Kranz
77
Maintainer: Sebastian Kranz <[email protected]>
88
Description: Shiny is a great package. Yet, for more complex

README.md

+105-74
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,15 @@ app$ui = fluidPage(
6161
)
6262

6363
# Handler for the plot button
64-
buttonHandler("plotBtn", function(session, id, value, app,...) {
65-
setText("myText", paste0("You pressed the button ",id," ",
66-
value," times. "))
64+
buttonHandler("plotBtn", function(session, id,...) {
65+
restore.point("plotBtnClick")
66+
setText("myText", paste0("You pressed the button ",id," at ", Sys.time()))
6767
setPlot("myPlot", plot(runif(10), runif(10)))
6868
})
6969

7070
# Handler for change of an input value
71-
changeHandler("mySelect", function(id, value,...) {
71+
selectChangeHandler("mySelect", function(id, value,...) {
72+
restore.point("selectChange")
7273
setText("myText",paste0("You chose the list item ", value,". ",
7374
"A random number: ", sample(1:1000,1)))
7475
})
@@ -77,57 +78,123 @@ changeHandler("mySelect", function(id, value,...) {
7778
setText("myText","This is the start text...")
7879

7980
# Directly launch the events app in the viewer pane
80-
runEventsApp(app,launch.browser=rstudio::viewer)
81+
viewApp(app)
8182
```
8283

83-
Note that a call to `eventsApp()` stores the generated app object (an environment) globally. The calls to buttonHandler, changeHandler and setText reference by default to this globally stored app object. Once the app starts, a copy of the app object will be generated for each user session that is generated by shiny.
84+
Note that the handlers only are called when indeed an action is performed, e.g. the user selects a different element in a selectInput. In contrast, I often experienced in the usual approach in shiny to observe an input element, the change events are fired more often, e.g. when the element is first created.
8485

85-
Such a simple app app could be much easier written with the standard reactivity model of shiny. Yet, shinyEvents can become more useful when you have an app that creates a lot of dynamic UI.
86+
I put in every handler a <a href="https://github.com/skranz/restorepoint" target="_blank">restore point</a>, which I personally find very helpful to debug shiny applications.
8687

87-
## A simple app with dynamic UI
88+
## An app with form input
8889

89-
Here is a simple app that creates dynamic UI.
90+
Often I find it helpful to write apps with traditional input forms, where values from input fields are submitted when a button is pressed. Here is an example:
9091

9192
```r
93+
library(shinyEvents)
94+
9295
# Create a new eventsApp
9396
app = eventsApp()
97+
# create a namespace for ui widgets
98+
ns = NS("test-app")
9499

95-
# main ui
96100
app$ui = fluidPage(
97-
actionButton("uiBtn", "make dynamic ui"),
98-
textOutput("myText"),
99-
uiOutput('myUI')
101+
textInput("input_name","Your Name"),
102+
textInput("input_email", "Your Email"),
103+
simpleButton("btn","Submit", form.ids = c("input_name","input_email"),),
104+
textInput("output_name","Saved Name"),
105+
textInput("output_email","Saved Email")
106+
)
107+
108+
buttonHandler("btn", function(formValues, app=getApp(),...) {
109+
restore.point("btn.click")
110+
vals = formValues
111+
# Name after input_
112+
names(vals) = substring(names(vals),7)
113+
print(vals)
114+
setWidgetValues(list(
115+
output_name = vals$name,
116+
output_email = vals$email
117+
))
118+
})
119+
120+
viewApp(app)
121+
```
122+
123+
The `simpleButton` function has an argument `form.ids`, which lists the ids of all form elements whose values shall be passed to the buttonHandler in the list variable `formValues`.
124+
125+
## A more complex example with dynamic UI and custom HTML
126+
127+
The two examples before could also be easily implemented with a traditional shiny app. Here is a more complex example with a dynamically created HTML table with multiple inputs:
128+
129+
```r
130+
library(shinyEvents)
131+
132+
# Create a new eventsApp
133+
app = eventsApp()
134+
135+
app$ui = fluidPage(
136+
simpleButton("btn1","Create Excercises"),
137+
uiOutput("exUI")
100138
)
101139

102-
# Dynamically create UI with button and add handler for it
103-
buttonHandler("uiBtn", function(session, value,...) {
104-
# Set a new dynamic UI
105-
dynUI= fluidRow(
106-
actionButton("dynBtn", paste0("Dynamic button ",value))
140+
141+
buttonHandler("btn1", function(...) {
142+
restore.point("btn1_click")
143+
# Create manual HTML code
144+
# A table with 5 random addition exercises
145+
num1 = sample(1:100,5)
146+
num2 = sample(1:100,5)
147+
148+
rows = paste0(collapse = "\n",
149+
"<tr><td>",num1," + ", num2 ," = </td>",
150+
"<td><input class='ans-input' id='ans-",1:5,"',></input></td></tr>"
151+
)
152+
tab = paste0("<table>",rows,"</table>")
153+
ui = tagList(
154+
HTML(tab),
155+
uiOutput("ansUI"),
156+
simpleButton("btn2","Check Solution", form.sel=".ans-input")
107157
)
108-
setUI("myUI", dynUI)
109158

110-
# Add handler for the new dynBtn in the new UI.
111-
# Existing handlers for dynBtn are by default replaced
112-
buttonHandler("dynBtn", ui.count = value, function(value,ui.count,...) {
113-
setText("myText", paste0(
114-
"UI was created ", ui.count, " times.\n",
115-
"Dynamic button pressed ", value, " times."))
159+
# clear ansUI if button is pressed multiple times
160+
setUI("ansUI","")
161+
162+
buttonHandler("btn2", function(formValues, app=getApp(),...) {
163+
restore.point("btn2.click")
164+
vals = as.integer(unlist(formValues))
165+
166+
correct = sum(vals==num1+num2,na.rm = TRUE)
167+
setUI("ansUI", p(paste0(correct, " exercises correct.")))
116168
})
169+
170+
171+
setUI("exUI", ui)
117172
})
118-
# Directly launch the events app in the viewer pane
119-
runEventsApp(app,launch.browser=rstudio::viewer)
173+
174+
175+
viewApp(app)
120176
```
121177

122-
The button handler for the static button creates and sets a new UI with another button and also generates a handler for the new button.
178+
When we press `btn1`, we generate some custom HTML table from pure HTML code, not using any specific shiny widget. The dynamically generated simpleButton `btn2` uses the argument `form.sel=".ans-input"`. This is a <a href="https://www.w3schools.com/cssref/css_selectors.asp">CSS selector</a> and specifies that the values of all inputs with class `ans-input` shall be passed in the variable `formValues` to the buttonHandler of `btn2`.
123179

124-
Note:
180+
Note that the button handler for `btn1` is defined before the app runs, while the button handler for `btn2` is dynamically created during runtime. `shinyEvents` is designed to allow both. There is a difference in so far that all handlers that will be generated before the app starts will be available in every instance of the app. In contrast, handlers that will be added dynamically will only be available for the specific instance of the app, in which the handler was generated.
181+
182+
As a general observation, I tend to use more custom HTML and Javascript code the more shiny apps I have programmed. `shinyEvents` has correspondingly evolved to allow simple interfaces to such custom HTML, as the example above illustrates.
183+
184+
## Custom Event Handlers
185+
186+
For all sorts of <a href="https://api.jquery.com/category/events/">jQuery Events</a>, you can add a custom event handler with shinyEvents. Here is a custom handler, you can add to the previous example:
187+
188+
```r
189+
customEventHandler("ans_edit", css.locator=".ans-input",event = "keyup", function(id, value, data, ...) {
190+
restore.point("ans-edit")
191+
row = data$row
192+
setUI("ansUI", p(paste0("You typed ", value, " in row ", row)))
193+
})
194+
```
195+
196+
The handler is called when a jQuery `keyup` event is triggered on our manually generated input fields specified by the css class `ans-input`. If you look at the HTML code of our input fields, you see that we added a field `data-row`. By default all data fields of the element that triggers an element will be passed to the handler in the list variable `data`. Here we stored the row number and can now access it.
125197

126-
- The syntax for creating handlers (and setting values) stays the same for dynamic objects created by an already running app as for static objects that are created before the app has started.
127-
128-
- The dynamically created button Handler
129-
`buttonHandler("dynBtn", ui.count = value, function(value,ui.count...) {`
130-
passes a manual parameter ui.count to the handler function.
131198

132199

133200
## A small chat app
@@ -185,13 +252,12 @@ The code below generates a small chat application as a shiny events app in which
185252

186253
# Initialize each new session with a random user name
187254
appInitHandler(function(input, output, session,app,...) {
188-
updateTextInput(session,"userName",
189-
value=paste0("guest", sample.int(10000,1)) )
255+
updateTextInput(session,"userName",value=paste0("guest", sample.int(10000,1)) )
190256
updateAceEditor(session,editorId = "convAce",value = app$glob$txt)
191257
})
192258

193259

194-
runEventsApp(app, launch.browser=TRUE)
260+
viewApp(app, launch.browser=TRUE)
195261
# To test chat function, open several browser tabs
196262
```
197263

@@ -225,47 +291,12 @@ shinyServer(app$server)
225291
The generation of the app can then be put into global.R. If our first example, we would put into global.R:
226292

227293
```r
228-
# global.R for simply shiny events app
294+
# global.R for a simply shiny events app
229295

230296
library(shinyEvents)
231-
232-
# Create a new eventsApp
233297
app = eventsApp()
234-
235-
# ui
236-
app$ui = fluidPage(
237-
actionButton("plotBtn", "plot"),
238-
selectInput("mySelect", "Select:",
239-
c("Cylinders" = "cyl",
240-
"Transmission" = "am",
241-
"Gears" = "gear")
242-
),
243-
textOutput("myText"),
244-
plotOutput("myPlot")
245-
)
246-
247-
# Handler for the plot button
248-
buttonHandler("plotBtn", function(session, id, value, app,...) {
249-
setText("myText", paste0("You pressed the button ",id," ",
250-
value," times. "))
251-
setPlot("myPlot", plot(runif(10), runif(10)))
252-
})
253-
254-
# Handler for change of an input value
255-
changeHandler("mySelect", function(id, value,...) {
256-
setText("myText",paste0("You chose the list item ", value,". ",
257-
"A random number: ", sample(1:1000,1)))
258-
})
259-
260-
# Set an initial text
261-
setText("myText","This is the start text...")
298+
app$ui = fluidPage(p("Hello World!"))
262299

263300
# Important that you add this line
264301
appReadyToRun(app)
265302
```
266-
267-
Beware:
268-
269-
- Don't manually overwrite `app$server`. The server function will be created when creating a new app with `eventsApp()`. It contains code that is neccessary for running the app. If you want to add manual code when initializing the app, call the function `appInitHandler` as illustrated in the previous example. When using R Studio's functionality to run apps, you may have to leave out the argument `app` in the function passed to `appInitHandler`.
270-
271-

0 commit comments

Comments
 (0)