Skip to content

Commit 291d78a

Browse files
committed
Much more verbose version (not yet complete but much better)
1 parent 191a5c6 commit 291d78a

File tree

6 files changed

+159
-7
lines changed

6 files changed

+159
-7
lines changed

_config.yml

+4
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ gems:
55
title: "Ocsigen Blog"
66
url: https://ocsigen.github.io
77
baseurl: ""
8+
9+
syntax-highlighting:
10+
enabled: true
11+
css: syntax.css

_drafts/react-example-todomvc.md

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
title: TodoMVC example, a React version
3+
layout: default
4+
author: Stéphane Legrand
5+
---
6+
7+
[TodoMVC](http://todomvc.com/) is a project which offers the same Todo application implemented using MV* concepts in most of the popular JavaScript MV* frameworks. One of the aim of this project is to give a possibility to make a fair comparison between several frameworks by coding the same application. A js_of_ocaml version using the React module is now available. Check out the [source code](https://github.com/slegrand45/examples_ocsigen/tree/master/jsoo/todomvc-react) and the [demo](http://slegrand45.github.io/examples_ocsigen.site/jsoo/todomvc-react/). Comments and pull requests are welcome!
8+
9+
#### MVC
10+
11+
[MVC](https://en.wikipedia.org/wiki/Model-view-controller) stands for Model-View-Controller. It's a commonly used software architecture for implementing user interfaces. The application is divided in three components:
12+
13+
- the Model manages the data, logic and rules of the application ;
14+
- the Controller manages events from the view and accordingly updates the model ;
15+
- the View generates an output presentation (a web page for instance) based on the model data.
16+
17+
For the Todo application, we have three corresponding modules. [Model](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L38) contains mainly the tasks list and the new task field value. It uses the deriving feature of jsoo to convert the data to json and vice versa in order to be able to save/restore the application state. Otherwise this module is written with basic OCaml code. [Controller](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L111) produces new models according to the actions it receives. Whenever a new model is built, the module also sets it as the new reactive signal value, we will detail this point later. [View](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L175) builds the HTML to display the page, it takes the dynamic data from the model. The HTML contains also the events management code needed to emit the corresponding actions.
18+
19+
Besides these three MVC modules, the application uses also two helpers. [Storage](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L14) contains the functions to read/write a string value in the browser local storage. This module is used to save/restore the application data in json format. [Action](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L94) contains all the actions available for the user interface.
20+
21+
#### React
22+
23+
[React](http://erratique.ch/software/react) is an OCaml module for [functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming) (FRP). In this TodoMVC example, React is used to automatically refresh the view whenever a new model is built by the controller.
24+
25+
#### Mixing the two
26+
27+
The following figure shows what happens when the user interacts with the application:
28+
29+
![MVC with React](/img/posts/2015/react-example-todomvc-steps.png)
30+
31+
1. the view sends the action to the controller ;
32+
2. the controller gets the current model and build a new one accordingly to the action ;
33+
3. the controller sets this new model as the new reactive signal value ;
34+
4. the view detects that a new model is available (the view reacts to the new signal value) and updates itself with the new model data.
35+
36+
Let's now see how this is implemented. Please note that i will only detail the reactive features.
37+
38+
### Initialization
39+
40+
The [main function](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L393) creates the reactive signal with an initial model (possibly empty). The `m` value is of type `Model.t`:
41+
42+
{% highlight ocaml %}
43+
let rp = React.S.create m in
44+
{% endhighlight %}
45+
46+
React.S.create returns a tuple. The first value is a primitive signal. The second value is a function which will be used later by the controller [to set a new model as the new signal value](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L170).
47+
48+
### Reactive attribute
49+
50+
This first example explains how the CSS style of a HTML node becomes reactive ([source code](https://github.com/slegrand45/examples_ocsigen/blob/d6766d404a449d0b1d36ad3cd916b0c444390a19/jsoo/todomvc-react/todomvc.ml#L267-L299)). In the Todo application, the tasks list is displayed in a `<section>` HTML tag. The CSS style of this HTML node must contain `visibility: hidden;` if the tasks list is empty. But must contain `visibility: visible;` if the number of tasks is greater than zero. So the style attribute of this `<section>` node must change regarding the model content:
51+
52+
{% highlight ocaml %}
53+
R.Html5.a_style (React.S.map css_visibility r)
54+
{% endhighlight %}
55+
56+
The first thing to note is that we use the Tyxml_js.R.Html5 module instead of the Tyxml_js.Html5 one. Tyxml_js.R.Html5 is simply the Reactive counterpart of Tyxml_js.Html5. As it's a reactive attribute, the a_style function waits a reactive signal as its argument. Here we use React.S.map which have the signature `('a -> 'b) -> 'a React.signal -> 'b React.signal`. This map function takes as its first argument a function named css_visibility:
57+
58+
{% highlight ocaml %}
59+
let css_visibility m =
60+
let tasks = m.Model.tasks in
61+
match tasks with
62+
| [] -> "visibility: hidden;"
63+
| _ -> "visibility: visible;"
64+
{% endhighlight %}
65+
66+
As you can see, this function takes a model `m` as its argument. In fact, thanks to React.S.map, it takes the signal value as its argument. And then the function returns the right style regarding if the tasks list is empty or not.
67+
68+
The second argument to React.S.map is the value named `r`. Remember when we created the reactive signal with React.S.create? This `r` is simply the first returned value, the primitive signal.
69+
70+
So each time the signal value will be updated by the controller, the css_visibility function will be automatically called with the new signal value (a new model) as its argument and the style attribute will be automatically modified.
71+
72+
### Reactive list
73+
74+
TODO
75+
76+
### Signal typing
77+
78+
TODO
79+
80+
{% highlight ocaml %}
81+
type rs = Model.t React.signal
82+
type rf = ?step:React.step -> Model.t -> unit
83+
type rp = rs * rf
84+
{% endhighlight %}

_includes/header.html

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
<!-- Custom styles for this template -->
2222
<link href="/css/bootstrap_custom.css" rel="stylesheet">
2323

24+
<!-- Code syntax style: https://raw.githubusercontent.com/aahan/pygments-github-style/master/jekyll-github.css -->
25+
<link href="/css/syntax.css" rel="stylesheet">
26+
2427
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements
2528
and media queries -->
2629
<!--[if lt IE 9]>

_posts/2015-08-30-react-example-todomvc.md

-7
This file was deleted.

css/syntax.css

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* GitHub style for Pygments syntax highlighter, for use with Jekyll
3+
* Courtesy of GitHub.com
4+
*/
5+
6+
.highlight pre, pre, .highlight .hll { background-color: #f8f8f8; border: 1px solid #ccc; padding: 6px 10px; border-radius: 3px; }
7+
.highlight .c { color: #999988; font-style: italic; }
8+
.highlight .err { color: #a61717; background-color: #e3d2d2; }
9+
.highlight .k { font-weight: bold; }
10+
.highlight .o { font-weight: bold; }
11+
.highlight .cm { color: #999988; font-style: italic; }
12+
.highlight .cp { color: #999999; font-weight: bold; }
13+
.highlight .c1 { color: #999988; font-style: italic; }
14+
.highlight .cs { color: #999999; font-weight: bold; font-style: italic; }
15+
.highlight .gd { color: #000000; background-color: #ffdddd; }
16+
.highlight .gd .x { color: #000000; background-color: #ffaaaa; }
17+
.highlight .ge { font-style: italic; }
18+
.highlight .gr { color: #aa0000; }
19+
.highlight .gh { color: #999999; }
20+
.highlight .gi { color: #000000; background-color: #ddffdd; }
21+
.highlight .gi .x { color: #000000; background-color: #aaffaa; }
22+
.highlight .go { color: #888888; }
23+
.highlight .gp { color: #555555; }
24+
.highlight .gs { font-weight: bold; }
25+
.highlight .gu { color: #800080; font-weight: bold; }
26+
.highlight .gt { color: #aa0000; }
27+
.highlight .kc { font-weight: bold; }
28+
.highlight .kd { font-weight: bold; }
29+
.highlight .kn { font-weight: bold; }
30+
.highlight .kp { font-weight: bold; }
31+
.highlight .kr { font-weight: bold; }
32+
.highlight .kt { color: #445588; font-weight: bold; }
33+
.highlight .m { color: #009999; }
34+
.highlight .s { color: #dd1144; }
35+
.highlight .n { color: #333333; }
36+
.highlight .na { color: teal; }
37+
.highlight .nb { color: #0086b3; }
38+
.highlight .nc { color: #445588; font-weight: bold; }
39+
.highlight .no { color: teal; }
40+
.highlight .ni { color: purple; }
41+
.highlight .ne { color: #990000; font-weight: bold; }
42+
.highlight .nf { color: #990000; font-weight: bold; }
43+
.highlight .nn { color: #555555; }
44+
.highlight .nt { color: navy; }
45+
.highlight .nv { color: teal; }
46+
.highlight .ow { font-weight: bold; }
47+
.highlight .w { color: #bbbbbb; }
48+
.highlight .mf { color: #009999; }
49+
.highlight .mh { color: #009999; }
50+
.highlight .mi { color: #009999; }
51+
.highlight .mo { color: #009999; }
52+
.highlight .sb { color: #dd1144; }
53+
.highlight .sc { color: #dd1144; }
54+
.highlight .sd { color: #dd1144; }
55+
.highlight .s2 { color: #dd1144; }
56+
.highlight .se { color: #dd1144; }
57+
.highlight .sh { color: #dd1144; }
58+
.highlight .si { color: #dd1144; }
59+
.highlight .sx { color: #dd1144; }
60+
.highlight .sr { color: #009926; }
61+
.highlight .s1 { color: #dd1144; }
62+
.highlight .ss { color: #990073; }
63+
.highlight .bp { color: #999999; }
64+
.highlight .vc { color: teal; }
65+
.highlight .vg { color: teal; }
66+
.highlight .vi { color: teal; }
67+
.highlight .il { color: #009999; }
68+
.highlight .gc { color: #999; background-color: #EAF2F5; }
Loading

0 commit comments

Comments
 (0)