Bacon.js Tutorial Part II: Get Started

In my previous blog posting posting, I introduced a Registration Form application case study, and then hacked it together with just jQuery, with appalling results. Now we shall try again, using Bacon.js. It'll take a while, of course, but the result will be quite pleasing, so please be patient. We'll start from the very basics.
This is how you implement an app with Bacon.js.
  1. Capture input into EventStreams and Properties
  2. Transform and compose signals into ones that describe your domain.
  3. 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 are EventStreams and the gray boxes are Properties. 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 username and 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.
git clone git@github.com:raimohanska/bacon-devday-code.git
cd bacon-devday-code
git co -t origin/clean-slate
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 asEventStream, 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
$("#username input").asEventStream("keyup")
And you'll get an EventStream of the jQuery keyup events. Try this in your browser Javascript console:
$("#username input").asEventStream("keyup").log()
Now the events will be logged into the console, whenever you type something to the username field (try!). To define the usernameproperty, we'll transform this stream into a stream of textfield values (strings) and then convert it into a Property:
$("#username input").asEventStream("keyup").map(function(event) { return $(event.target).val() }).toProperty("")
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 map method 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?
The username property is in fact ready for use. Just name it and copy it to the source code:
username = $("#username input").asEventStream("keyup").map(function(event) { return $(event.target).val() }).toProperty("")
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:
function textFieldValue(textField) {
    function value() { return textField.val() }
    return textField.asEventStream("keyup").map(value).toProperty(value())

username = textFieldValue($("#username input"))
fullname = textFieldValue($("#fullname input"))
Better! In fact, there's already a textFieldValue function available in Bacon.UI, and it happens to be incluced in the code already so you can just go with
username = Bacon.UI.textFieldValue($("#username input"))
fullname = Bacon.UI.textFieldValue($("#fullname input"))
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 to both the username and fullname fields.
I'll start by defining the buttonEnabled Property:
function and(a,b) { return a && b }
buttonEnabled = usernameEntered.combine(fullnameEntered, and)
So I defined the Property by combining to props together, with the and function. The combine method works so that when eitherusernameEntered and fullnameEntered changes, the result Property will get a new value. The new value is constructed by applying the and function to the values of both props. Easy! And can be even easier:
buttonEnabled = usernameEntered.and(fullnameEntered)
This does the exact same thing as the previous one, but relies on the boolean-logic methods (andornot) included in Bacon.js.
But something's still missing. We haven't defined usernameEntered and fullnameEntered. Let's do.
function nonEmpty(x) { return x.length > 0 }
usernameEntered = username.map(nonEmpty)
fullnameEntered = fullname.map(nonEmpty)
buttonEnabled = usernameEntered.and(fullnameEntered)
So, we used the map method again. It's good to know that it's applicable to both EventStreams and Properties. And thenonEmpty function is actually already defined in the source code, so you don't actually have to redefine it.
The side-effect part is simple:
buttonEnabled.onValue(function(enabled) {
    $("#register button").attr("disabled", !enabled)
Try it! Now the button gets immediately disabled and will enabled once you type something to both the text fields. Mission accomplished!
But we can do better.
For example,
buttonEnabled.not().onValue($("#register button"), "attr", "disabled")
This relies on te fact that the onValue method, like many other Bacon.js methods, supports different sets of parameters. On 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
buttonEnabled.assign(setEnabled, registerButton)
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.


  1. Actually, the last version of disabling contains a bug: since the negation of `enabled` is done inside setEnabled, the not() causes incorrect behavior.

    This is correct:

    buttonEnabled.assign(setEnabled, registerButton)

  2. Oops! I fixed it.

    Also, published the result after Tutorial Part II:


  3. Unrelated to the Bacon. In Chrome the content table on the right jumping 1 pixel up and down instantly.

  4. @sasha not entirely unrelated - having event cycles and jumping content is one hazard of FRP to be aware of ;)

  5. It strikes me as odd that in the following code snippet:
    function textFieldValue(textField) {
    function value() { return textField.val() }
    return textField.asEventStream("keyup").map(value).toProperty(value())

    You pass a parameterless function (i.e. value) as an argument to map. I am no JavaScript expert but I know that in some languages (i.e. Scheme for example) this would not be allowed because map expects a function of the type (a->b) as an argument whereas the value function in this example is of type ( -> b).

    Could you elaborate on this?
    Thank you.

    1. That's a Javascript thing. If I'm not mistaken, it's really a dynamically-typed language thing. You can define/re-define function signatures dynamically.

    2. That's a Javascript thing. If I'm not mistaken, it's really a dynamically-typed language thing. You can define/re-define function signatures dynamically.

  6. This comment has been removed by the author.

  7. Janne's correction to the
    buttonEnabled.assign(setEnabled, registerButton)
    code is still incorrect on the official BaconJS tut page:

    I'm also confused with the section that reads "Now we rely on the setEnabled function that's defined in our source code" ...

    Where is that defined? It seems like its the first time it was even mentioned. So is this just assuming that the dev would define that as a function? or is this a help function in Bacon.... sorry, when I learn things for the first time, it's hard to to take things quite literally.

  8. So... I've read this series of blog posts, the baconjs api, the FAQ, and... I still can't find the difference between a Stream and a Property.

    Some libraries (I'm thinking highlandjs in particular) treat everything as a stream. I'm guessing that "Properties" just hide boilerplate code from users.

  9. So... I've read this series of blog posts, the baconjs api, the FAQ, and... I still can't find the difference between a Stream and a Property.

    Some libraries (I'm thinking highlandjs in particular) treat everything as a stream. I'm guessing that "Properties" just hide boilerplate code from users.

  10. Really awesome blog. Your blog is really useful for me. Thanks for sharing this informative blog. Keep update your blog...Java Training in Chennai
    Java Training Institute in Chennai

  11. Nice looking sites and great work. Pretty nice information. it has a better understanding. thanks for spending time on it.

    Hadoop Training Institute in Noida

    Best Hadoop Training in Noida

  12. Excellent blog, I wish to share your post with my folks circle. It’s really helped me a lot, so keep sharing post like this
    Java training in Chennai

    Java training in Bangalore

  13. Good job in presenting the correct content with the clear explanation. The content looks real with valid information. Good Work

    DevOps is currently a popular model currently organizations all over the world moving towards to it. Your post gave a clear idea about knowing the DevOps model and its importance.

    Good to learn about DevOps at this time.

    devops training in chennai | devops training in chennai with placement | devops training in chennai omr | devops training in velachery | devops training in chennai tambaram | devops institutes in chennai | devops certification in chennai | trending technologies list 2018 | Top 100 DevOps Interview Questions and Answers

  14. Outstanding blog thanks for sharing such wonderful blog with us ,after long time came across such knowlegeble blog. keep sharing such informative blog with us.

    machine learning workshops in chennai
    machine learning projects in chennai
    machine learning tution in chennai
    artificial intelligence and machine learning course in chennai

  15. One of the best content i have found on internet for Data Science training in Chennai .Every point for Data Science training in Chennai is explained in so detail,So its very easy to catch the content for Data Science training in Chennai .keep sharing more contents for Trending Technologies and also updating this content for Data Science and keep helping others.
    Cheers !
    Thanks and regards ,
    Data Science course in Velachery
    Data Scientists course in chennai
    Best Data Science course in chennai
    Top data science institute in chennai

  16. Really useful information. Thank you so much for sharing.It will help everyone.Keep Post. RPA training in chennai | RPA training in Chennai with placement