├── favicon.ico
├── apple-icon.png
├── img
├── egghead.png
└── cyclejs_logo.svg
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon-96x96.png
├── ms-icon-144x144.png
├── ms-icon-150x150.png
├── ms-icon-310x310.png
├── ms-icon-70x70.png
├── apple-icon-57x57.png
├── apple-icon-60x60.png
├── apple-icon-72x72.png
├── apple-icon-76x76.png
├── android-icon-144x144.png
├── android-icon-192x192.png
├── android-icon-36x36.png
├── android-icon-48x48.png
├── android-icon-72x72.png
├── android-icon-96x96.png
├── apple-icon-114x114.png
├── apple-icon-120x120.png
├── apple-icon-144x144.png
├── apple-icon-152x152.png
├── apple-icon-180x180.png
├── apple-icon-precomposed.png
├── .gitignore
├── .editorconfig
├── _sass
├── typography.sass
├── dimens.sass
├── colors.sass
├── mixins.sass
├── vendor.sass
├── documentation.sass
├── basic.sass
└── homepage.sass
├── main.sass
├── 404.html
├── browserconfig.xml
├── README.md
├── _includes
├── footer.html
├── img
│ ├── modules-foo-bar.svg
│ ├── passive-foo-bar.svg
│ ├── reactive-foo-bar.svg
│ ├── simple-human-computer.svg
│ ├── view-translation.svg
│ ├── intent-translation.svg
│ ├── completed-stream.svg
│ ├── main-eq-mvi.svg
│ ├── egghead.svg
│ ├── human-computer-diagram.svg
│ ├── fold-counter.svg
│ ├── mvc-diagram.svg
│ ├── nested-components.svg
│ ├── dataflow-component.svg
│ ├── main-domdriver-side-effects.svg
│ ├── hci-inputs-outputs.svg
│ ├── actuators-senses-input-output.svg
│ ├── actuators-senses.svg
│ ├── custom-element-drivers.svg
│ ├── dataflow-minimap.svg
│ └── cycle-nested-frontpage.svg
└── homepage-content.md
├── _config.yml
├── archive.html
├── manifest.json
├── LICENSE
├── index.html
├── _posts
├── 2015-01-21-community.md
├── 2015-01-22-documentation.md
├── 2015-01-30-dialogue.md
├── 2015-01-31-getting-started.md
├── 2015-01-29-streams.md
├── 2015-01-23-drivers.md
└── 2015-01-26-model-view-intent.md
└── _layouts
├── default.html
└── blank.html
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/favicon.ico
--------------------------------------------------------------------------------
/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon.png
--------------------------------------------------------------------------------
/img/egghead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/img/egghead.png
--------------------------------------------------------------------------------
/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/favicon-16x16.png
--------------------------------------------------------------------------------
/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/favicon-32x32.png
--------------------------------------------------------------------------------
/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/favicon-96x96.png
--------------------------------------------------------------------------------
/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/ms-icon-144x144.png
--------------------------------------------------------------------------------
/ms-icon-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/ms-icon-150x150.png
--------------------------------------------------------------------------------
/ms-icon-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/ms-icon-310x310.png
--------------------------------------------------------------------------------
/ms-icon-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/ms-icon-70x70.png
--------------------------------------------------------------------------------
/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-57x57.png
--------------------------------------------------------------------------------
/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-60x60.png
--------------------------------------------------------------------------------
/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-72x72.png
--------------------------------------------------------------------------------
/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-76x76.png
--------------------------------------------------------------------------------
/android-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/android-icon-144x144.png
--------------------------------------------------------------------------------
/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/android-icon-192x192.png
--------------------------------------------------------------------------------
/android-icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/android-icon-36x36.png
--------------------------------------------------------------------------------
/android-icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/android-icon-48x48.png
--------------------------------------------------------------------------------
/android-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/android-icon-72x72.png
--------------------------------------------------------------------------------
/android-icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/android-icon-96x96.png
--------------------------------------------------------------------------------
/apple-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-114x114.png
--------------------------------------------------------------------------------
/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-120x120.png
--------------------------------------------------------------------------------
/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-144x144.png
--------------------------------------------------------------------------------
/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-152x152.png
--------------------------------------------------------------------------------
/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-180x180.png
--------------------------------------------------------------------------------
/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyclejs/old-website/HEAD/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .sass-cache/
3 | .DS_Store
4 | _site/
5 |
6 | # Editors
7 | .idea/
8 |
9 | .jekyll-metadata
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | indent_style = space
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/_sass/typography.sass:
--------------------------------------------------------------------------------
1 |
2 | $body-font: "Source Sans Pro", "Calibri", "Helvetica", sans-serif
3 | $header-font: "Merriweather", "Lora", "Georgia", serif
--------------------------------------------------------------------------------
/_sass/dimens.sass:
--------------------------------------------------------------------------------
1 |
2 | $padding: 33%
3 | $small-space: 1rem
4 | $normal-space: 2.5rem
5 | $large-space: 4rem
6 | $side-menu-width: 220px
7 | $total-page-width: 880px
8 | $max-article-width: 720px
9 |
--------------------------------------------------------------------------------
/main.sass:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | @import colors
5 | @import dimens
6 | @import typography
7 | @import mixins
8 | @import basic
9 | @import homepage
10 | @import documentation
11 | @import vendor
12 |
--------------------------------------------------------------------------------
/404.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 | 404
8 |
9 | {% include img/simple-human-computer.svg %} 10 |
11 | 12 | The computer usually "speaks" visual information through a screen, which the human can "listen". Through different devices, the computer can reach different human senses. The human, on the other hand, often "speaks" commands to the computer through its hand, manipulating a mouse or touching a screen. 13 | 14 | #### Human-Computer Interaction is a dialogue: an ongoing exchange of messages between both sides. 15 | 16 | Both parties are able to "listen" to each other, and utter messages through their devices. In other words, we can say the human and the computer are mutually observed, or simply having a dialogue. The reason why we point out "mutual observation" is because it is related to Reactive Programming and will affect how we model this system. 17 | 18 |19 |30 | 31 | The computer is made of devices to interact with the human. *Output* devices present information to the human, and *input* devices detect actions from the human. The human possesses *actuators* and *senses*, which are connected to the computer's *input* and *output* devices, respectively. 32 | 33 |Similarity with Haskell 1.0?
20 |21 | This same dialogue concept can be found in Haskell 1.0 Stream-based I/O, where
23 |type Dialogue = [Response] -> [Request]
is the model of interaction with the Operating System.[Response]is a stream of messages from the OS, and[Request]is a stream of messages to the OS. 22 |24 | Cycle.js' abstraction was discovered independently from Haskell's Stream I/O. We try to take some inspiration from Haskell's
26 |Dialoguewhenever convenient, but there are a few conceptual differences. Not all problems with Haskell'sDialogueexist in Cycle.js or matter to Cycle.js users, and vice-versa. This is due to different execution environment assumptions and different design decisions on modelling event streams. If you want more details on this topic, the following GOTO talk is recommended, centered around the history and theory behind Cycle.js: 25 |27 | 28 |
29 |
34 | {% include /img/actuators-senses-input-output.svg %} 35 |
36 | 37 | The *inputs* and *outputs* of the computer suggest the computer's role in HCI can be expressed as a function. 38 | 39 | {% highlight js %} 40 | function computer(inputDevices) { 41 | // define the behavior of `outputDevices` somehow 42 | return outputDevices; 43 | } 44 | {% endhighlight %} 45 | 46 | We do not yet know what `inputDevices` and `outputDevices` should be in JavaScript, but for now try to appreciate the elegance of `computer()` as a pure function. Refactoring and architecture is just a matter of choosing the right composition of functions to make up `computer()`. 47 | 48 | When talking about inputs, outputs, senses, and actuators, it becomes difficult to describe the difference between senses and inputs, other than the former is often associated with humans, and the latter with computers. From the computer's perspective, a microphone and its drivers are how the computer is able to *sense* auditory information. In essence, when we ignore the nature of the human body and the physics of computing machines, the human and the computer are both simply agents with senses and actuators. 49 | 50 | {% highlight js %} 51 | function computer(senses) { 52 | // define the behavior of `actuators` somehow 53 | return actuators; 54 | } 55 | {% endhighlight %} 56 | 57 | The agents in this interaction are now **symmetric**: the actuator of one is connected to the senses of the other, and conversely. 58 | 59 |60 | {% include /img/actuators-senses.svg %} 61 |
62 | 63 | The diagram above is an [anthropomorphism](https://en.wikipedia.org/wiki/Anthropomorphism) of the computer. If we take the opposite approach of objectifying humans as machines, then both human and computer would be functions with inputs and outputs. 64 | 65 |66 | {% include /img/hci-inputs-outputs.svg %} 67 |
68 | 69 | Which suggests the human would be a function from its senses to its actuators. 70 | 71 | {% highlight js %} 72 | function human(senses) { 73 | // define the behavior of `actuators` somehow 74 | return actuators; 75 | } 76 | {% endhighlight %} 77 | 78 |79 |87 | 88 | While these abstractions seem to be natural choices for user interfaces, many questions still remain: 89 | 90 | - What are the types of `senses` and `actuators`? 91 | - When is the `human()` function called? 92 | - When is the `computer()` function called? 93 | - If the output of one is the input of the other, how do we solve the circular dependency `y = human(x)` and `x = computer(y)`? 94 | 95 | These are questions that drive the core architecture of Cycle.js, but to understand our solution, we first need to understand reactive streams: our building block for everything in Cycle.js. [Keep reading](/streams.html). 96 | -------------------------------------------------------------------------------- /_includes/img/main-domdriver-side-effects.svg: -------------------------------------------------------------------------------- 1 | 75 | -------------------------------------------------------------------------------- /_includes/img/hci-inputs-outputs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_includes/img/actuators-senses-input-output.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_includes/img/actuators-senses.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_includes/img/custom-element-drivers.svg: -------------------------------------------------------------------------------- 1 | 74 | -------------------------------------------------------------------------------- /_includes/homepage-content.md: -------------------------------------------------------------------------------- 1 |JSConf Budapest talk
80 |81 | Watch Andre Staltz's talk on What if the user was a function? which addresses the same topics as this chapter does. 82 |
83 |84 | 85 |
86 |
4 | {% include /img/cycle-nested-frontpage.svg %} 5 |
6 | 7 | Cycle's core abstraction is your application as a pure function `main()` where inputs are read effects (*sources*) from the external world and outputs (*sinks*) are write effects to affect the external world. These I/O effects in the external world are managed by *drivers*: plugins that handle DOM effects, HTTP effects, etc. 8 | 9 | The internals of `main()` are built using Reactive Programming primitives, which maximizes separation of concerns and provides a fully declarative way of organizing your code. The *dataflow* is plainly visible in the code, making it readable and traceable. 10 | 11 | ## Example 12 | 13 | {% highlight text %} 14 | npm install xstream @cycle/xstream-run @cycle/dom 15 | {% endhighlight %} 16 | 17 | {% highlight js %} 18 | import {run} from '@cycle/xstream-run'; 19 | import {div, label, input, hr, h1, makeDOMDriver} from '@cycle/dom'; 20 | 21 | function main(sources) { 22 | const sinks = { 23 | DOM: sources.DOM.select('.field').events('input') 24 | .map(ev => ev.target.value) 25 | .startWith('') 26 | .map(name => 27 | div([ 28 | label('Name:'), 29 | input('.field', {attrs: {type: 'text'}}), 30 | hr(), 31 | h1('Hello ' + name), 32 | ]) 33 | ) 34 | }; 35 | return sinks; 36 | } 37 | 38 | run(main, { DOM: makeDOMDriver('#app-container') }); 39 | {% endhighlight %} 40 | 41 | 42 | 43 |68 | {% include /img/dataflow-minimap.svg %} 69 |
70 |103 | {% include /img/nested-components.svg %} 104 |
105 | 106 | Sources and sinks are the interface between the application and the drivers, but they are also the interface between a child component and its parent. Cycle.js components can be simply GUI widgets like in other frameworks, but they are not limited to GUIs only. You can make Web Audio components, network requests components, and others, since the sources/sinks interface is not exclusive to the DOM. 107 | 108 | ## Learn it in 1h 37min 109 | 110 |111 | {% include /img/egghead.svg %} 112 |
113 | 114 | Got 1 hour and 37 minutes? That is all it takes to learn the essentials of Cycle.js. Watch [**this free Egghead.io video course**](https://egghead.io/series/cycle-js-fundamentals) given by the creator of Cycle.js. Understand Cycle.js from within by following how it is built from scratch, then learn how to transform your ideas into applications. 115 | 116 | 117 | 118 | ## Supports... 119 | 120 | - [**Virtual DOM rendering**](https://github.com/cyclejs/cyclejs/tree/master/dom) 121 | - [**Server-side rendering**](https://github.com/cyclejs/cyclejs/tree/master/examples/isomorphic) 122 | - [**JSX**](http://cycle.js.org/getting-started.html) 123 | - [**TypeScript**](https://github.com/cyclejs/cyclejs/tree/master/examples/bmi-typescript) 124 | - [**React Native**](https://github.com/cyclejs/cycle-react-native) 125 | - [**Time traveling**](https://github.com/cyclejs/cycle-time-travel) 126 | - [**Routing with the History API**](https://github.com/cyclejs/history) 127 | - [**And more...**](https://github.com/cyclejs-community/awesome-cyclejs) 128 | -------------------------------------------------------------------------------- /_posts/2015-01-31-getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Getting Started" 3 | tags: chapters 4 | --- 5 | 6 |` DOM elements, but you can also use JSX with Babel. The following only works if you are building with Babel: (1) install the npm packages [babel-plugin-transform-react-jsx](http://babeljs.io/docs/plugins/transform-react-jsx/) and [snabbdom-jsx](https://www.npmjs.com/package/snabbdom-jsx); (2) specify a pragma for JSX as shown in the following example `.babelrc` file: 127 | 128 | {% highlight json %} 129 | { 130 | "presets": [ 131 | "es2015" 132 | ], 133 | "plugins": [ 134 | "syntax-jsx", 135 | ["transform-react-jsx", {"pragma": "html"}] 136 | ] 137 | } 138 | {% endhighlight %} 139 | 140 | (3) import Snabbdom JSX as `import {html} from 'snabbdom-jsx';`, and then you can utilize JSX: 141 | 142 | {% highlight html %} 143 | import xs from 'xstream'; 144 | import {run} from '@cycle/xstream-run'; 145 | import {makeDOMDriver} from '@cycle/dom'; 146 | import {html} from 'snabbdom-jsx'; 147 | 148 | function main(sources) { 149 | const sinks = { 150 | DOM: sources.DOM.select('input').events('click') 151 | .map(ev => ev.target.checked) 152 | .startWith(false) 153 | .map(toggled => 154 |
{toggled ? 'ON' : 'off'}
157 |21 | {% include img/modules-foo-bar.svg %} 22 |
23 | 24 | A practical example of such arrow would be: *whenever Foo does a network request, increment a counter in Bar*. If all code lives in some module, **where does this arrow live?** Where is it defined? The typical choice would be to write code inside Foo which calls a method in Bar to increment the counter. 25 | 26 | {% highlight js %} 27 | // This is inside the Foo module 28 | 29 | function onNetworkRequest() { 30 | // ... 31 | Bar.incrementCounter(); 32 | // ... 33 | } 34 | {% endhighlight %} 35 | 36 | Because Foo owns the relationship "*when network request happens, increment counter in Bar*", we say the arrow lives at the arrow tail, i.e., Foo. 37 | 38 |39 | {% include img/passive-foo-bar.svg %} 40 |
41 | 42 | Bar is **passive**: it allows other modules to change its state. Foo is proactive: it is responsible for making Bar's state function correctly. The passive module is unaware of the existence of the arrow which affects it. 43 | 44 | The alternative to this approach inverts the ownership of the arrow, without inverting the arrow's direction. 45 | 46 |47 | {% include img/reactive-foo-bar.svg %} 48 |
49 | 50 | With this approach, Bar listens to an event happening in Foo, and manages its own state when that event happens. 51 | 52 | {% highlight js %} 53 | // This is inside the Bar module 54 | 55 | Foo.addOnNetworkRequestListener(() => { 56 | self.incrementCounter(); // self is Bar 57 | }); 58 | {% endhighlight %} 59 | 60 | Bar is **reactive**: it is fully responsible for managing its own state by reacting to external events. Foo, on the other hand, is unaware of the existence of the arrow originating from its network request event. 61 | 62 | What is the benefit of this approach? It is Inversion of Control, mainly because Bar is responsible for itself. Plus, we can hide Bar's `incrementCounter()` as a private function. In the passive case, it was required to have `incrementCounter()` public, which means we are exposing Bar's internal state management outwards. It also means if we want to discover how Bar's counter works, we need to find all usages of `incrementCounter()` in the codebase. In this regard, Reactive and Passive seem to be dual to each other. 63 | 64 | | | Passive | Reactive | 65 | |-----------------------|-------------------------|---------------| 66 | | How does Bar work? | *Find usages* | Look inside | 67 | 68 | On the other hand, when applying the Reactive pattern, if you want to discover which modules are affected by an event in a Listenable module, you must find all usages of that event. 69 | 70 | | | Proactive | Listenable | 71 | |-----------------------------|-------------------------|---------------| 72 | | Which modules are affected? | Look inside | *Find Usages* | 73 | 74 | Passive/Proactive programming has been the default way of working for most programmers in imperative languages. Sometimes the Reactive pattern is used, but sporadically. The selling point for widespread Reactive programming is to build self-responsible modules which focus on their own functionality rather than changing external state. This leads to Separation of Concerns. 75 | 76 | The challenge with Reactive programming is this paradigm shift where we attempt to choose the Reactive/Listenable approach by default, before considering Passive/Proactive. After rewiring your brain to think Reactive-first, the learning curve flattens and most tasks become straightforward, especially when using a Reactive library like RxJS or *xstream*. 77 | 78 |91 | {% include img/completed-stream.svg %} 92 |
93 | 94 | Streams can be listened to, just like EventEmitters and DOM events can. 95 | 96 | {% highlight js %} 97 | myStream.addListener({ 98 | next: function handleNextEvent(event) { 99 | // do something with `event` 100 | }, 101 | error: function handleError(error) { 102 | // do something with `error` 103 | }, 104 | complete: function handleCompleted() { 105 | // do something when it completes 106 | }, 107 | }); 108 | {% endhighlight %} 109 | 110 | Notice there are 3 handlers: one for events, one for errors, and one for "complete". 111 | 112 | *xstream* Streams become very useful when you transform them with the so-called *operators*, pure functions that create new Streams on top of existing ones. Given a Stream of click events, you can easily make a Stream of the number of times the user clicked. 113 | 114 | {% highlight js %} 115 | const clickCountStream = clickStream 116 | // each click represents "1 amount" 117 | .mapTo(1) 118 | // sum all events `1` over time, starting from 0 119 | .fold((count, x) => count + x, 0); 120 | {% endhighlight %} 121 | 122 | [Succinctness is Power](http://www.paulgraham.com/power.html), and *xstream* operators demonstrate that you can achieve a lot with a few well-placed operators. With only about [26 operators](https://github.com/staltz/xstream#methods-and-operators), you can build almost all programming patterns needed in a Cycle.js app. 123 | 124 | Knowing the basics of reactive streams programming is a prerequisite to getting work done with Cycle.js. Instead of teaching RxJS or *xstream* on this site, we recommend a few great learning resources, in case you need to learn more. *xstream* is similar to *RxJS*, so these resources apply: 125 | 126 | - [The introduction to Reactive Programming you've been missing](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754): a thorough introduction to RxJS by Cycle.js author Andre Staltz. 127 | - [Introduction to Rx](http://introtorx.com/): an online book focused on Rx.NET, but most concepts map directly to RxJS. 128 | - [ReactiveX.io](http://reactivex.io/): official cross-language documentation site for ReactiveX. 129 | - [Learn Rx](http://reactivex.io/learnrx/): an interactive tutorial with arrays and Observables, by Jafar Husain. 130 | - [RxJS lessons at Egghead.io](https://egghead.io/technologies/rx) 131 | - [RxJS GitBook](http://xgrommx.github.io/rx-book/) 132 | - [RxMarbles](http://rxmarbles.com/): interactive diagrams of RxJS operators, built with Cycle.js. 133 | - [Async JavaScript at Netflix](https://www.youtube.com/watch?v=XRYN2xt11Ek): video of Jafar Husain introducing RxJS. 134 | 135 |29 | {% include /img/human-computer-diagram.svg %} 30 |
31 | 32 | However, in practice, we write our `main()` function targeted at a `domDriver()`. For a user interacting with a browser, we only need to make our `main()` interact with the DOM. Whenever we need to show something to the user, we instead show that to the DOM, and the DOM together with the browser shows that to our user. When we need to detect the user's interaction events, we attach event listeners on the DOM, and the DOM will notify us when the user interacts with the browser on the computer. 33 | 34 |35 | {% include /img/main-domdriver-side-effects.svg %} 36 |
37 | 38 | Notice there are two directions of interaction with the external world through the DOM. The *write* effect is the renderization of our Snabbdom VNodes to DOM elements which can be shown on the user's screen. The *read* effect is the detection of DOM events generated by the user manipulating the computer. 39 | 40 | The `domDriver()` manages these two effects while allowing them to be interfaced with the `main()`. The *input* to `domDriver()` captures instructions for the *write* effect, and the *read* effect is exposed as the *output* of `domDriver()`. The anatomy of the `domDriver()` function is roughly the following: 41 | 42 | {% highlight js %} 43 | function domDriver(vdom$) { 44 | // Use vdom$ as instructions to create DOM elements 45 | // ... 46 | return { 47 | select: function select(selector) { 48 | // returns an object with two functions: `events()` 49 | // and `elements()`. Function `events(eventType)` 50 | // returns the stream of `eventType` DOM events 51 | // happening on the elements matched by `selector`. 52 | // Function `elements()` is the stream of DOM 53 | // elements matching the given `selector`. 54 | } 55 | }; 56 | } 57 | {% endhighlight %} 58 | 59 | The input `vdom$` is the output from `main()`, and the output of `domDriver()` is the input to `main()`: 60 | 61 | {% highlight js %} 62 | function main(sources) { 63 | // Use sources.DOM.select(selector).events(eventType) 64 | // ... 65 | // Create vdom$ somehow 66 | // ... 67 | return { 68 | DOM: vdom$ 69 | }; 70 | } 71 | {% endhighlight %} 72 | 73 | As a recap: 74 | 75 | - `main()`: takes **sources** as input, returns **sinks** 76 | - `domDriver()`: takes **sinks** as input, performs write and read effects, returns **sources**. 77 | 78 |