Backbone.Syphon: Serialize Form Inputs To JavaScript Objects

Working with form elements in a Backbone view can become very tedious very quickly. You will either end up writing a lot of repetitive code to read values from the form, or end up using a key-value-observer or data-binding solution that automatically populates your model for you. While these are valid options and I highly recommend understanding how they work, there are times when these options are not the best choice for your application.

jQuery

The most basic option in serializing a form’s elements in to use jQuery’s serialize or serializeArray methods. These methods work well and make it easy to get data out of your form. But they have limitations that I don’t like:

The serialize method encodes the values in to a URL encoding. This works well if you plan on using the data as URL parameters in an HTTP GET or other similar manner. But if you want to use the data in your JavaScript code, it’s a bit of a pain to work with.

The serializeArray method return a JavaScript array that contains objects each with a name and value key. So, a text input with a “name” of “foo” would return [{name: "foo", value: "bar"}]. This works much better for dealing with the data in your JavaScript code, but is still severely constrained. You don’t have direct access to the key/value pairs based on the name of the input element. Instead, you have to search through the array of objects and find the one you want. 

What I want, instead, is a way to serialize a form into a plain JavaScript object where the object’s attributes (or keys) are the input element ids, with the key’s value set to the value of the input element.

Backbone.Syphon

Backbone.Syphon aims to make it easy to serialize the form inputs of a Backbone.View in to a simple JSON object that contains all of the values from the form. All you need to do is call `Backbone.Syphon.serialize(view)` and it will return a JavaScript object to you with key / value pairs that match the input element ids and values.

For example, with this underscore template:

<script id="my-form-template" type="text/html">
  <form>
    <input type="text" id="foo" value="bar">
    <input type="checkbox" id="chk" checked>
  </form>
</script>

And this Backbone.View setup:

Backbone.View.extend({
  events: {
    "submit form": "formSubmitted"
  },

  formSubmitted: function(e){
    e.preventDefault();

    var data = Backbone.Syphon.serialize(this);
    this.model.set(data);

    this.model.save();
  },

  render: function(){
    // build the view's form, here
  }
});

When you click the “save” button, the handler for the click calls `Backbone.Syphon.serialize(this)`. This returns an object that looks like this:

{
  foo: "bar",
  chk: true
}

The large benefit of this output is that you can immediately pass it to a Backbone.Model instance via the `set` method. There’s no need to sift through it and pull out the bits you need, like the jQuery serialize methods. Of course you can use it for more than just Backbone models, too. Anywhere that you need the data from your form, you can get it with Syphon and use it right away.

I use this plugin in several of my projects already – well, precursors that were poorly written and not tested thoroughly, at least. But I plan on continuing to use this plugin in scenarios where I need to get data out of my forms. Data-binding plugins just don’t interest me anymore, as I’ve gone down that path and abandoned it. 

Serializes By Element Id

There are a handful of limitations in this first release. One of the largest is that all of the input elements will be serialized by the `id` attribute of the element. Look at the example above again, and you’ll notice that the text input with an id of “foo” was serialized in to a `{foo: “bar”}` attribute on the resulting object. I’m sure this will change over time, and become something more configurable. But for now, this was all I needed as I tend to use the id of a field as the key for my object’s data.

For more information on the current limitations, see the documentation.

Some-what Pluggable Serialization

I’ve built this plugin more than a few times at this point, starting with direct client needs and eventually turning in to the plugin that it is now. One of the lessons that I learned in rebuilding it a few times is that the process of serializing the input elements needs to be pluggable. I can’t limit how it reads the data from the input elements, because there are different needs for different situations. 

With that in mind, I built in the idea of “Input Readers“. An Input Reader is a callback function that is registered against a particular input type, and is used to read the data from that type of input. 

At the moment, there are only two built in input readers: the default, which handles nearly everything, and the checkbox reader. The default reader uses jQuery’s `val()` function to get the value out of the input. This works for nearly every type of input. But there are some cases where this isn’t what you want to do. For example, I generally use checkboxes as boolean values instead of an actual text value. To handle this, then, I created the built in checkbox input reader. It uses jQuery’s `prop()` function to read the state of the checkbox and tell me whether or not the checkbox is checked. If you look at the previous example again, you’ll see that the “chk” checkbox is returning a boolean value.

Initial Build And Release As A Screencast

There are several reasons that this initial release is so small and limited in scope and functionality. The largest of which is that it covers the majority of my needs, and like all of my plugins and add-ons, I figure out what I need and the common things that I do across projects first. Then when I have the majority of my cases covered, I start looking at what others need in order to get their projects up and running.

Another reason that this release is limited, is because I decided to record the creation of the plugin as a live screencast – a “prequel” of sorts, to the first “Refactoring JavaScript” screencast that I released last year. In this case, the end product of the Backbone.Syphon plugin is more of a side effect than the purpose of the screencast. The real focus of the screencast is the tools and processes that I use to get a plugin like this off the ground, built and delivered. 

If you’re an audio/video learner and you want to see my process for creating something from nothing more than the ideas in my head, then you should check out Episode 7 of WatchMeCode: Building A Backbone Plugin (Live). And if you’re looking for a way to get up to speed with test-driven JavaScript, including a basic introduction to Jasmine and refactoring an existing application that is covered by Jasmine tests, then you might be interested in the Jasmine Live! multi-pack of screencasts.

Documentation, Downloads, And Moving Forward

If you’d like to get your hands on Syphon, head over to the Github repository. There are links to the the downloads and a little bit of documentation to show how to use it (the same as what I showed here), as well as some discussion on the current limitations of the plugin.

I’m hoping to get some input and assistance from others that could use a plugin like this, moving forward. I’d like to see this project fleshed out so that it can be used in a broader set of scenarios, with more options for how the data is serialized from the input elements. At the same time, though, I’m going to be rather picky about how the implementation moves forward. I don’t want to end up with another 1,000 line blob of unmaintainable garbage, like my ModelBinding plugin. As it stands now, though, Syphon’s code is starting out on the right foot. I just need to keep it heading down a clean path as it moves forward.

 


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, Backbone.Syphon, Javascript, JQuery, JSON. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://twitter.com/wvl wvl

    I’m not sure there is any need for this to be backbone specific. Surely any backbone integration could be solved by a small plugin.

    I’ve been using form2js[1] for this quite successfully — it’s handled everything I’ve thrown at it. The only thing I’d change about it is that by default it omits empty inputs (skipEmpty option), where I’d like the default to be an empty string. It uses the name attribute for the key (falls back to using id if name isn’t present), and handles nested objects and arrays nicely.

    [1] - https://github.com/maxatwork/form2js

  • Anonymous

    I was just thinking about building something like this – nice work. Definitely needs to base off of ‘name’ instead of ‘id’, or at least be configurable (as you mentioned); I have places in my app where I have, say, an ‘email’ field twice on the same page.

    • http://mutedsolutions.com Derick Bailey

      awesome :) thanks for the pull request! i’ll try to get to it this weekend. i’ve got a few other things i need to put in place, too.

  • http://twitter.com/liammclennan liammclennan

    What if you based this on $.serializeArray()? That would give you quite a bit for free

    http://jsfiddle.net/ynkJE/5/ 

    • http://mutedsolutions.com Derick Bailey

      very nice!

      a tab limited, based on what jQuery can do – but still a very nice solution! definitely thinking about this some more

      • Anonymous

        I built this in coffeescript, using serializearray and underscore: https://gist.github.com/2721165

        I put it on a base view class that my views inherit.

        • http://twitter.com/tgriesser Tim Griesser

          I created something similar to yours, but taking into account form arrays: https://gist.github.com/9eabb684587bb529e287 … i typically just mix it into underscore

      • http://twitter.com/liammclennan liammclennan

        I’m going to convert mine to a jquery plugin. This seems more of a jquery function than a backbone one. 

        • http://mutedsolutions.com Derick Bailey

          I’ve been leaning toward that in the back of my mind, too. Still thinking.

          • Anonymous

            Why does everything have to be a “plugin” to a preexisting library? This is just a function. It has nothing to do with Backbone or jQuery or anything else.

          • http://twitter.com/liammclennan liammclennan

            It works with the DOM so it is directly related to jquery. Using a jquery plugin is a nice convention that helps people understand how to use it. It also provides nice syntax $(‘form’).slurp();

  • Anonymous

    Have you looked at Ben Alman’s serializeObject?
    http://benalman.com/projects/jquery-misc-plugins/#serializeobject

    I’ve used it for a long time (even before Backbone) and it works really well with Backbone.

  • Chris Hoffman

    Totally get you on avoiding databinding.  Way too Webforms-y if you ask me. This approach seems more elegant.

  • http://twitter.com/josemotanet José Mota

    I was looking for this for some time. I’m using RequireJS to build my app. Have you tried to plug something into Backbone using it?

    • http://mutedsolutions.com Derick Bailey

      Syphon has a RequireJS compatible build, listed in the readme: https://github.com/derickbailey/backbone.syphon#amdrequirejs-builds

      that should work for you. there are a lot of people using Backbone with RequireJS, and most of my Backbone plugins have RequireJS compatible builds at this point.

      • http://twitter.com/josemotanet José Mota

        That’s awesome, I appreciate you doing this. Will try it later on.

      • http://twitter.com/josemotanet José Mota

        Derick, sorry to bother you again. Do I need to manually require Syphon everytime I need it or is there some sort of mechanism to have it available everytime I require Backbone?

        • http://mutedsolutions.com Derick Bailey

          You’ll have to require it every time you need it. that’s one of the limitations of requirejs.

  • http://thomasdavis.github.com/ Thomas Davis

    Cool work! The current way the library is used means it might as well be a jQuery library. I’d think it would be cool if the plugin hooked into the response such that if a ‘submit form’ event detected then you would have access to the serialized form data in the function arguments.

  • http://www.facebook.com/jan.roesner Jan Roesner

    Derick, could it be, that Syphon does not work well with Backbone 0.9.2? Followed your instructions but serialize is always undefined.

  • Michael

    This is great Derick, thanks for building it!

  • Motoprog

    The ability to override the default KeySplitter to use a more vanilla JSON object notation for name values e.g. foo.bar.baz, to the rails standard of foo[bar][baz] opens up a whole knew world. This function alone in my app made this project worth its salt:
    “`
    Backbone.Syphon.KeySplitter = function(key){
    return key.split(“.”);
    }

    “`

  • George Jempty

    Just sprinkle a little underscore on the data returned by jQuery’s serializeArray and it is no longer “severely constrained”, or at least your concern goes away: _.object(_.pluck(serializedArray, ‘name’), _.pluck(serializedArray, ‘value’)) and presto! you can then obtain values from the returned object using name as the key.

  • http://xeoncross.com Xeoncross

    I don’t know if it still uses ID’s but – using this.el context should be enough along with names to have multiple forms on a single page without worrying about using “#id-names” more than once.

  • Uchiha Itachi