-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathtest.html
128 lines (112 loc) · 6.91 KB
/
test.html
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
<!DOCTYPE html>
<!-- This file is generated. See package.json -->
<html>
<head>
<title>Test markdown</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="keywords" content="bacon.js, FPR, functional reactive programming, javascript, reactive programming" />
<meta name="description" content="Bacon.js is a functional reactive programming library for javascript." />
<link rel="stylesheet" type="text/css" href="normalize.css" >
<link rel="stylesheet" type="text/css" href="foundation.min.css" >
<link rel="stylesheet" type="text/css" href="codemirror/codemirror.css" >
<link rel="stylesheet" type="text/css" href="marble.css">
<link rel="stylesheet" type="text/css" href="bacon.css" >
<link href='//fonts.googleapis.com/css?family=Yanone+Kaffeesatz' rel='stylesheet' type='text/css'>
<script src="//codeorigin.jquery.com/jquery-2.1.1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bacon.js/0.7.41/Bacon.min.js"></script>
<script src="codemirror/codemirror.js"></script>
<script src="codemirror/javascript.js"></script>
<script src="marble.js"></script>
<script src="example.js"></script>
</script>
</head>
<body>
<div class="grid row">
<div class="large-4 columns sidebar">
<div class="logo">
<a href="index.html"><img alt="bacon.js logo" src="logo.png" /></a>
</div>
<div class="download">
<a class="button big-button" href="http://cdnjs.cloudflare.com/ajax/libs/bacon.js/0.7.41/Bacon.js" download>
Download Bacon.js
<small>v0.7.41</small>
</a>
<a class="github" href="https://github.com/baconjs/bacon.js">Or get it on GitHub</a>
</div>
<div class="menu">
<div class="row">
<div class="large-12 small-6 columns">
<p> <a href="api.html"> API Reference </a> </p>
<p> Tutorials </p>
<p> <a href="https://github.com/baconjs/bacon.js">Install/Download</a> </p>
</div>
<div class="large-12 small-6 columns">
<p> Develop </p>
<p> Bacon cookbook </p>
<p> <a href="https://github.com/baconjs/bacon.js/wiki/FAQ">FAQ</a> </p>
</div>
</div>
</div>
</div>
<div class="large-8 columns">
<div class="main">
<h1>Structuring Real-Life Applications</h1>
<p>The Internet is full of smart peanut-size examples of how to solve X with "FRP" and Bacon.js. But how to organize a real-world size application? That's been <a href="https://github.com/baconjs/bacon.js/issues/478">asked</a> once in a while and indeed I have an answer up in my sleeve. Don't take though that I'm saying this is the The Definitive Answer. I'm sure your own way is as good or better. Tell me about it!</p>
<p>I think there are some principles that you should apply to the design of any application though, like <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle">Single Reponsibility Principle</a> and <a href="http://en.wikipedia.org/wiki/Separation_of_concerns">Separation of Concerns</a>. Given that, your application should consist of components that are fairly independent of each others implementation details. I'd also like the components to communicate using some explicit signals instead of shared mutable state (nudge nudge Angular). For this purpose, I find the Bacon.js <code>EventStreams</code> and <code>Properties</code> quite handy.</p>
<p>So if a component needs to act when a triggering event occurs, why not give it an <code>EventStream</code> representing that event in its constructor. The <code>EventStream</code> is an abstraction for the event source, so the implementation of your component is does not depend on where the event originates from, meaning you can use a WebSocket message as well as a mouse click as the actual event source. Similarly, if you component needs to display or act on the <em>state</em> of something, why not give it a <code>Property</code> in its constructor.</p>
<p>When it comes to the outputs of a component, those can exposed as <code>EventStreams</code> and <code>Properties</code> in the component's public interface. In some cases it also makes sense to publish a <code>Bus</code> to allow plugging in event sources after component creation.</p>
<p>For example, a ShoppingCart model component might look like this.</p>
<pre><code class="language-javascript"><textarea class="code">function ShoppingCart(initialContents) {
var addBus = new Bacon.Bus()
var removeBus = new Bacon.Bus()
var contentsProperty = Bacon.update(initialContents,
addBus, function(contents, newItem) { return contents.concat(newItem) },
removeBus, function(contents, removedItem) { return _.remove(contents, removedItem) }
)
return {
addBus: addBus,
removeBus: removeBus,
contentsProperty: contentsProperty
}
}
</textarea></code></pre>
<p>Internally, the ShoppingCart contents are composed from an initial status and <code>addBus</code> and <code>removeBus</code> streams using <code>Bacon.update</code>.</p>
<p>The external interface of this component exposes the <code>addBus</code> and <code>removeBus</code> buses where you can plug external streams for adding and removing items. It also exposes the current contents of the cart as a <code>Property</code>.</p>
<p>Now you may define a view component that shows cart contents, using your favorite DOM manipulation technology, like <a href="https://github.com/Matt-Esch/virtual-dom">virtual-dom</a>:</p>
<pre><code class="language-javascript"><textarea class="code">function ShoppingCartView(contentsProperty) {
function updateContentView(newContents) { /* omitted */ }
contentsProperty.onValue(updateContentView)
}
</textarea></code></pre>
<p>And a component that can be used for adding stuff to your cart:</p>
<pre><code class="language-javascript"><textarea class="code">function NewItemView() {
var $button, $nameField // JQuery objects
var newItemProperty = Bacon.$.textFieldValue($nameField) // property containing the item being added
var newItemClick = $button.asEventStream("click") // clicks on the "add to cart" button
var newItemStream = newItemProperty.sampledBy(newItemClick)
return {
newItemStream: newItemStream
}
}
</textarea></code></pre>
<p>And you can plug these guys together as follows.</p>
<pre><code class="language-javascript"><textarea class="code">var cart = ShoppingCart([])
var cartView = ShoppingCartView(cart.contentsProperty)
var newItemView = NewItemView()
cart.addBus.plug(newItemView.newItemStream)
</textarea></code></pre>
<p>So there you go!</p>
</div>
</div>
</div>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-58079902-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>