Skip to content
Browse files

Adding docs for the Select HOWTO.

  • Loading branch information...
1 parent a28c6d7 commit 3d2e2c1af5911785fd0b1022a2ad9cd880841e05 @akoprow committed
Showing with 96 additions and 13 deletions.
  1. +96 −13 select.opa
View
109 select.opa
@@ -1,38 +1,119 @@
import stdlib.themes.bootstrap
-function select(({string id} or {autogen}) id, list('a) options, ('a -> void) callback) {
- id =
+/**
+ * This is the core function here. It takes a list of options and let's the user
+ * select one of them. Upon selecting any item it calls the supplied callback
+ * with the selected value.
+ *
+ * @param id The id of the selection element; you can provide either [{id: value}]
+ * to generate an element with the given [value] id, or [{autogen}],
+ * in which case a (random) id will be generated by this function.
+ * @param options A list of values of an arbitrary type. Those will be the values
+ * that the user can choose from.
+ * @param callback A callback that will be called every time user makes selection.
+ * It has a single argument, which is the selected value.
+ * @return The function returns XHTML snippet with the selection among given options.
+ *
+ * Exercises:
+ * - As it is this function just renders the options with a default conversion
+ * to xhtml (which btw. can be customized with xmlizer functions!); what would
+ * you do if you need more control over the XHTML generated for every option?
+ * Try implementing it!
+ * - The callback is called every time the user changes his selection. Another
+ * useful interface would be to be able at any time to learn what's the user's
+ * selection. How would you provide this functionality?
+ *
+ * HINT: Opa is fully type-safe, however interacting with DOM/user is not.
+ * Writing proper auxiliary functions, such as this one, to hide this
+ * fact from the rest of the program and provide type-safe abstraction
+ * is crucial (and one day will probably be accomplished by a UI library
+ * for Opa).
+ */
+function select( // HINT: Such variants arguments are great way to make functions
+ // more flexible and reduce the number of variants of functions
+ ({string id} or {autogen}) id,
+ list('a) options,
+ ('a -> void) callback
+ )
+{
+ // the final id of this element
+ sel_id =
match (id) {
- case {id: user_id}: user_id;
- case {autogen}: Dom.fresh_id()
+ case {id: user_id}: user_id; // take user provided value
+ case {autogen}: Dom.fresh_id() // take a fresh DOM value
}
+ // this function renders a single option; it also assigns to it a given value
function render_option(i, o) {
<option value={i}>{o}</>
}
+ // this function will be called on every selection change
function changed(_evt) {
- sel_val = List.unsafe_get(String.to_int(Dom.get_value(#{id})), options);
+ // when rendering we gave options successive numerical values, so to learn
+ // which one was selected we just take the value of the selection, convert
+ // it to numerical value 'n' and take n'th element from the provided list of
+ // options
+ sel_val = List.unsafe_get(String.to_int(Dom.get_value(#{sel_id})), options);
callback(sel_val);
}
- <select id={id} onchange={changed}>
+ // Finally the <select> itself, with [onchange] event and the list of options
+ <select id={sel_id} onchange={changed}>
{List.mapi(render_option, options)}
</>
}
-function selected(int number) {
- #selectMsg = <>You selected {number}</>
+/**
+ * Callback function for favorite number choice
+ *
+ * @param number Selected number
+ * @return Nothing, but as a side-effect changes the help text for the selection
+ * informing which number was chosen
+ */
+// HINT: we will use those as callback functions to select function above.
+// However, beware of putting those function inside the page function itself;
+// page is used to generate the HTML response to the client and hence will
+// live on the server. However, this callback function will be called every
+// time selection is changed so we better make sure it's on the client
+// (otherwise a round-trip to the server will occur with every selection
+// change). To do that we can annote it with the 'client' keyword, however
+// in this case it's not neccessary as Opa will put it on the client anyway.
+function selected_num(int number) {
+ #selectNumMsg = <>You selected number: {number}</>
}
+/**
+ * Callback function for favorite color choice
+ *
+ * @param color Selected color
+ * @return Nothing, but as a side-effect changes the help text for the selection
+ * informing which color was chosen
+ */
+function selected_color(string color) {
+ #selectColorMsg = <>You selected color: {color}</>
+}
+
+/**
+ * This is the main page. It's just a bunch of Bootstrap compatible XHTML markup,
+ * using the [select] function that we defined above.
+ */
function page() {
- list(int) numberRange = [2, 3, 4, 5, 6, 7, 8, 9];
+ list(int) numbers = [2, 3, 4, 5, 6, 7, 8, 9];
+ list(string) colors = ["Red", "Purple", "Pink", "Orange", "Yellow", "Green", "Blue"];
<div class=container>
<form class=form-horizontal>
<legend>Simple selection demo with Opa</>
<fieldset>
<div class=control-group>
- <label class=control-label for=select01>Make your choice</>
+ <label class=control-label for=select01>Your favorite number?</>
+ <div class=controls>
+ {select({id: "selectNum"}, numbers, selected_num)}
+ <p id=#selectNumMsg class=help-block>You did not make any selection</>
+ </>
+ </>
+ <div class=control-group>
+ <label class=control-label for=select01>Your favorite color?</>
<div class=controls>
- {select({id: "select01"}, numberRange, selected)}
- <p id=#selectMsg class=help-block>You did not make any selection</>
+ {select({id: "selectColor"}, colors, selected_color)}
+ <p id=#selectColorMsg class=help-block>You did not make any selection</>
</>
</>
</>
@@ -40,5 +121,7 @@ function page() {
</>
}
- // simple one-page server
+/**
+ * Finally, a simple one-page server
+ */
Server.start(Server.http, {title: "Form selection in Opa", ~page});

0 comments on commit 3d2e2c1

Please sign in to comment.
Something went wrong with that request. Please try again.