- Registration Form
- Ajax and Stuff
- Wrapping Things in Bacon
- Structuring Real-Life Applications
- Bus of Doom
In this tutorial I'll be building a fully functional, however simplified, AJAX registration form for an imaginary web site.
The registration form could look something like this:
Starting with the Naive Approach
This seems ridiculously simple, right? Enter username, fullname, click and you're done. As in
At first it might seem so, but if you're planning on implementing a top-notch form, you'll want to consider
- Username availability checking while the user is still typing the username
- Showing feedback on unavailable username
- Showing an AJAX indicator while this check is being performed
- Disabling the Register button until both username and fullname have been entered
- Disabling the Register button in case the username is unavailable
- Disabling the Register button while the check is being performed
- Disabling the Register button immediately when pressed to prevent double-submit
- Showing an AJAX indicator while registration is being processed
- Showing feedback after registration
Some requirements, huh? Still, all of these sound quite reasonable, at least to me. I'd even say that this is quite standard stuff nowadays. You might now model the UI like this:
Now you see that, for instance, enabling/disabling the Register button depends on quite a many different things, some of them asynchronous.
In my original blog posting I actually implemented these features using jQuery, with appalling results. Believe me, or have a look...
Back to Drawing Board
Generally, this is how you implement an app with Bacon.js.
- Capture input into EventStreams and Properties
- Transform and compose signals into ones that describe your domain.
- Assign side-effects to signals
In practise, you'll probably pick a single feature and do steps 1..3 for that. Then pick the next feature and so on until you're done. Hopefully you'll do some refactoring on the way to keep your code clean.
Sometimes it may help to draw the thing on paper. For our case study, I've done that for you:
In this diagram, the greenish boxes represent EventStreams (distinct events) and the gray boxes are Properties (values that change over time). The top three boxes represent the raw input signals:
- Key-up events on the two text fields
- Clicks on the register button
In this posting we'll capture the input signals, then define the
fullname properties. In the end, we'll be able to print the values to the
console. Not much, but you gotta start somewhere.
You can just read the tutorial, or you can try things yourself too. In case you prefer the latter, here are the instructions.
First, you should get the code skeleton on your machine.
So now you've cloned the source code and switched to the clean-slate branch. Alternatively you may consider forking the repo first and creating a new branch if you will.
Anyway, you can now open the
index.html in your browser to see the registration form.
You may also open the file in your favorite editor and have a brief look. You'll find some
helper variables and functions for easy access to the DOM elements.
Capturing Input from DOM Events
Bacon.js is not a jQuery plugin or dependent on jQuery in any way. However, if it finds
jQuery, it adds a method to the jQuery object prototype. This method is called
and it is used to capture events into an EventStream. It's quite easy to use.
To capture the
keyup events on the username field, you can do
And you'll get an
Now the events will be logged into the console, whenever you type something to the username field (try!).
To define the
username property, we'll transform this stream into a stream of textfield values (strings)
and then convert it into a Property:
To see how this works in practise, just add the
.log() call to the end and you'll see the results in your console.
What did we just do?
We used the
map method to transform each event into the current value of the username field. The
returns another stream that contains mapped values. Actually it's just like the map function
in underscore.js, but for EventStreams and Properties.
After mapping stream values, we converted the stream into a Property by calling
toProperty(""). The empty string is the initial value
for the Property, that will be the current value until the first event in the stream. Again, the toProperty method returns a
new Property, and doesn't change the source stream at all. In fact, all methods in Bacon.js
return something, and most have no side-effects. That's what you'd expect from a functional programming library, wouldn't you?
username property is in fact ready for use. Just name it and copy it to the source code:
I intentionally omitted "var" at this point to make it easier to play with the property in the browser developer console.
Next we could define
fullname similarly just by copying and pasting. Shall we?
Nope. We'll refactor to avoid duplication:
Better! In fact, there's already a
textFieldValue function available in Bacon.UI,
and it happens to be included in the code already so you can just go with
So, there's a helper library out there where I've shoveled some of the things that seems to repeat in different projects. Feel free to contribute!
Anyway, if you put the code above into your source code file, reload the page in the browser and type
to the developer console, you'll see username changes in the console log.
Mapping Properties and Adding Side-Effects
To get our app to actually do something visible besides writing to the console, we'll define a couple of new
Properties, and assign our first side-effect. Which is enabling/disabling the Register button based on whether
the user has entered something in both the username and fullname fields.
I'll start by defining the
So I defined the Property by combining the two props together, with the
combine method works so that when either
changes, the result Property will get a new value. The new value is constructed by applying
and function to the values of both props. Easy! And can be even easier:
This does the exact same thing as the previous one, but relies on the boolean-logic methods
not) included in Bacon.js.
But something's still missing. We haven't defined
fullnameEntered. Let's do.
So, we used the
map method again. It's good to know that it's applicable to both
nonEmpty function is actually already defined in the source code, so you don't actually have to redefine it.
The side-effect part is simple:
Try it! Now the button gets immediately disabled and will be enabled once you type something in both of the text fields. Mission accomplished!
But we can do better.
This relies on the fact that the
onValue method, like many other Bacon.js methods, supports different sets of
parameters. One of them is the above form, which can be translated as "call the
attr method of the register
button and use
disabled as the first argument". The second argument for the
attr method will be taken from the
current property value.
You could also do the same by
Now we rely on the
setEnabled function that's defined in our source code, as well as
registerButton. The above
can be translated to "call the
setEnabled function and use
registerButton as the first argument".
So, with some Bacon magic, we eliminated the extra anonymous function and improved readability. Om nom nom.
And that's it for now. We'll do AJAX soon.
Ajax and Stuff
This is the next step in the Bacon.js tutorial series. I hope you've read Part II already! This time we're going to implement an "as you type" username availability check with AJAX. The steps are
- Create an
EventStreamof AJAX requests for use with
- Create a stream of responses by issuing an AJAX request for each request event and capturing AJAX responses into the new stream.
- Define the
usernameAvailableProperty based on the results
- Some side-effects: disable the Register button if username is unavailable. Also, show a message.
I suggest you checkout the example code and switch to the tutorial-2 branch which will be the starting point for the coding part of this posting. If you just want to have a look at what we've got so far, have a peek.
So, at this point we've got a Property called
represents the current value entered to the username text input field.
We want to query for username availability each time this property
changes. First, to get the stream of changes to the property we'll do
This will return an
EventStream. The difference to the
Property itself is that there's no initial value (the empty string).
Next, we'll transform this to a stream that provides jQuery compatible
The next step is extremely easy, using Bacon.UI.js:
This maps the requests into AJAX responses. Looks very simple, but behind the scene it takes care of request/response ordering so that you'll only ever get the response of the latest issued request in that stream. This is where, with pure jQuery, we had to resort to keeping a counter variable for making sure we don't get the wrong result because of network delays.
So, what does the
ajax() method in Bacon.UI look like? Does it do
stuff with variables? Lets see.
Not so complicated after all. But let's talk about
flatMap now, for a
while, so that you can build this kind of helpers yourself, too.
AJAX as a Promise
AJAX is asynchronous, hence the name. This is why we can't use
convert requests into responses. Instead, each AJAX request/response
should be modeled as a separate stream. And it can be done too. So, if
you'll get a jQuery
Deferred object. This
object can be thought of as a
Promise of a result. Using
the Promise API, you can handle the asynchronous results by assigning a
callback with the
done(callback) function, as in
If you try this in you browser, you should see
true printed to the
console shortly. You can wrap any Promise into an EventStream using
Bacon.fromPromise(promise). Hence, the following will have the same
This is how you make an EventStream of an AJAX request.
So now we have a stream of AJAX requests and the knowhow to create a new stream from a jQuery AJAX. Now we need to
- Create a response stream for each request in the request stream
- Collect the results of all the created streams into a single response stream
This is where
flatMap comes in:
Now you'll have a new EventStream called
flatMap does is
- It calls your function for each value in the source stream
- It expects your function to return a new EventStream
- It collects the values of all created streams into the result stream
Like in this diagram.
So here we go. The only issue left is that
flatMap doesn't care about
response ordering. It spits out the results in the same order as they
arrive. So, it may (and will) happen that
- Request A is sent
- Request B is sent
- Result of request B comes
- Result of request A comes
.. and your
availabilityResponse stream will end with the wrong
answer, because the latest response is not for the latest request.
Fortunately there's a method for fixing this exact problem: Just replace
flatMapLatest (previously called "switch") and you're done.
Now that you know how it works, you may as well use the
that Bacon.UI provides:
The easy part : side-effects
Let's show the "username not available" message. It's actually a stateful thing, so we'll convert
availabilityResponse stream into a new Property:
The boolean value is used to give the property a starting value. So now this property starts with the value
and after that reflects that value from the
availabilityRequest stream. The visibility of the message element
should actually equal to the negation of this property. So why not something like
Once again, this is equivalent to
... the idea in the former being that we partially-apply
setVisibility function: Bacon will call the function with
unavailabilityLabel fixed as the first argument.
The second argument to the function will be the value from the
Finally, we'll also disable the Register button when the username is unavailable. This is done by changing
The result code can be found in the tutorial-3 branch.
Wrapping Things in Bacon
- JQuery events is supported out-of-the box
- With Bacon.JQuery, you get more, including AJAX, two-way bindings
- Node.js style callbacks, with (err, result) parameters, are supported with Bacon.fromNodeCallback
- General unary callbacks are supported with Bacon.fromCallback
- There's probably a plenty of wrappers I haven't listed here. Let's put them on the Wiki, shall we?
In case X doesn't fall into any of these categories, you may have to roll your own. And that's not hard either. Using
Bacon.fromBinder, you should be able to plug into any data source quite easily. In this blog posting, I'll show some examples of just that.
You might want to take a look at Bacon.js readme for documentation and reference.
Example 1: Timer
Let's start with a simple example. Suppose you want to create a stream that produces timestamp events each second. Easy!
Bacon.interval, you'll get a stream that constantly produces a value. Then you can use
map to convert the values into timestamps.
Bacon.fromPoll, you can have Bacon call your function each 1000 milliseconds, and produce the current timestamp on each call.
So, clearly Using
Bacon.fromBinder is an overkill here, but if you want to learn to roll your own streams, this might be a nice example:
Try it out: http://jsfiddle.net/PG4c4/
- you call
Bacon.fromBinderand you provide your own "subscribe" function
- there you register to your underlying data source. In the example,
- when you get data from your data source, you push it to the provided "sink" function. In the example, you push the current timestamp
- from your "subscribe" function you return another function that cleans up. In this example, you'll call
Example 2: Hammer.js
Hammer.js is a library for handling multi-touch gesture events. Just to prove my point, I created a fiddle where I introduce a "hammerStream" function that wraps any Hammer.js event into an EventStream:
Try it out: http://jsfiddle.net/axDJy/3/
It's exactly the same thing as with the above example. In my "subscribe" function, I register an event handler to Hammer.js. In this event handler I push a value "hammer time!" to the stream. I return a function that will de-register the hammer event handler.
You're not probably surprised at the fact that all the included wrappers and generators (including
Bacon.fromPoll etc) are implemented on top of Bacon.fromBinder. So, for more examples, just dive into the Bacon.js codebase itself.
Structuring Real-Life Applications
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 asked 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!
I think there are some principles that you should apply to the design of any application though, like Single Reponsibility Principle and Separation of Concerns. 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
Properties quite handy.
So if a component needs to act when a triggering event occurs, why not give it an
EventStream representing that event in its constructor. The
EventStream 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 your component needs to display or act on the state of something, why not give it a
Property in its constructor.
When it comes to the outputs of a component, those can exposed as
Properties in the component's public interface. In some cases it also makes sense to publish a
Bus to allow plugging in event sources after component creation.
For example, a ShoppingCart model component might look like this.
Internally, the ShoppingCart contents are composed from an initial status and
removeBus streams using
The external interface of this component exposes the
removeBus buses where you can plug external streams for adding and removing items. It also exposes the current contents of the cart as a
Now you may define a view component that shows cart contents, using your favorite DOM manipulation technology, like virtual-dom:
And a component that can be used for adding stuff to your cart:
And you can plug these guys together as follows.
So there you go!
Bus of Doom
In a previous Bacon blog post a way to structure Bacon application was outlined. It introduces Buses as a central way to glue components with each other. I'm in a very strong disagreement with the proposed style. Why?
Subjects are the "mutable variables" of the Rx world and in most cases you do not need them.
In Bacon parlance that would be
Buses are the "mutable variables" of the Bacon world and in most cases you do not need them.
Now, that needs an explanation. We can split the statement to two parts and treat each individually. "Why Buses (and mutable variables) are bad", then "why you don't usually need them".
Problems with Bus
There was a time when data structures in our programs were built by mutating those data structures. In case you have entered this field only recently you may be a lucky one and haven't seen that madness in full glory (== spaghetti).
This was a bad idea as it creates temporal dependencies all over the program, making it difficult to locally understand how a piece of code works. Instead, a global view on a program is required. Who mutates what and when. It also created many bugs as components of a system are from time to time in an invalid state. Most common invalid state being at a construction phase where fields are initialized to nulls. A whole slew of bugs were eliminated and sanity regained by moving to immutable data.
Ok, what does all that have to do with Buses? Well, Buses introduce similar temporal dependencies to your program. Is that component ready to be used? I don't know, did you plug its Buses already with this and that?
Here's a recent bug (simplified from a real world app) found in our company's internal chat. Can you spot it?
There's a chance that the ajax call on line 2 returns before line 4 is executed, thus the event is completely missed. It is temporal dependencies like that which are nearly impossible to understand in a bigger context. And what's worse, these bugs are difficult to reproduce as we are programming in a setting where stuff is nondeterministic (timers, delays, network calls etc.). I'm sure that many Bus fetished programs contain subtle bugs like above.
How to avoid Buses
I'll give examples of techniques avoiding Buses by refactoring the example in the previous blog post.
The first one is simple and obvious. Turn inputs of a component to be input arguments of the component.
I'm pretty sure everyone agrees that the refactored version is simpler.
The next refactoring has to do with remove links. Each shopping cart item will have a link and clicking a link will remove the item from a cart. The refactored version of the ShoppingCart needs
removeItem click stream as a function argument, but the individual items are created dynamically as the user selects items. This can be solved by event delegation.
You can state just once and for all: here's a stream of
clicks of every
.remove-item link in the shopping cart, and of all the future
.remove-item links that will appear in the shopping cart. That is fantastic. It's like, you put it there and there it is. Event delegation is such a god sent tool and my heart fills with joy every time I have a chance to use it. After that the click events must be associated with items. A canonical way to do it is with data attributes.
Now the sample program is Bus-free.
All done? Not yet.
removeItemStream is hanging there while it probably should be part of
Whoops, now we introduced a cyclic dependency between
Cyclic dependency is often given as an example where Buses are needed. After all the hard work should we now reintroduce Buses?
Here a Bus can be used to break the cyclic dependency, just as a mutable variable would do if you will. But we have other options too. Why don't we factor the components so that the cyclic dependency completely disappears.
Similar factorings can be almost always used to break cyclic dependencies.
Avoid Buses. View those as mutable variables and you will understand the kinds of problems they create. By relating Buses to mutable variables gives you an intuition on how to avoid those in a first place.