Archive for February, 2013

JQuery.obj() – Converting inputs to a javascript object

For a while now I’ve been using a JQuery extension that takes selected inputs (and dropdowns and textareas) or inputs contained in the selection and converts them into a javascript object ready for submission with an Ajax post as JSON.

The need for something like this came about when we found that the data that needed to be submitted didn’t neatly fit on a form. An example is where the data was across multiple tabs and the “submit” button needed to be on the first tab.

Now, we have the ability to use the following JQuery:

var newObj = $('#tab1,#tab2,#tab3').obj();

to grab all the inputs into an object, which can then be manipulated before submission. Inputs of type “button” and anything you mark with the class “ignore” won’t be pulled into the object.

The construction of the object is based on the names of the inputs, and follows the dot and bracket notation used in naming .Net MVC, so the following inputs:

<input name="m1.Sub1.Name" value="Anne" />
<input name="m1.Sub1.Age" value="22" />
<input name="m1.Sub1.Numbers[0]" value="1" />
<input name="m1.Sub1.Numbers[1]" value="2" />

will get converted to:

{ m1: { Sub1: { Name: "Anne", Age: "22", Numbers: [ "1", "2" ] } } }

unless of course the “preserveNaming” option is passed in and set to true. In which case the object will be flat, and you’ll get names like “m1.Sub1.Numbers[0]”. Using something like json2, you can stringify this and submit it in an ajax call. It plays very nicely with .Net MVC.

It’s worth pointing out that the JQuery selector can incorporate controls and containers, so you can do something like this:

var newObj = $('#myForm1,#myForm2,#aDropdown').obj({ preserveNaming: true });

and the “newObj” will incorporate all the bits.

So, onto the code, enjoy!:


// Use this jquery extension to convert the inputs, dropdowns & textareas on the selected node(s) into a javascript object
/// options:
///     preserveNaming: default = false - whether the function preserves the names of controls, or splits on .s an []s
(function ($) {

    var _options;

    $.fn.obj = function (options) {

        _options = options || {};

        var obj = {};

        // List of control names that have been seen.
        var namesSeen = [];

        this.each(function (index) {

            var selector = $(this);

            var jqSelector;
            if (selector.is('input') || selector.is('select') || selector.is('textarea')) {
                // This is a control with no inner html
                jqSelector = selector;
            } else {
                //This is a container
                jqSelector = $('input,select,textarea', selector);
            }

            // inputs
            jqSelector.each(function (index) {
                var nodeToCopy = $(this);

                // do not process controls with the ignore class
                // do not process buttons
                if (nodeToCopy.hasClass('ignore') || nodeToCopy.attr('type') == 'button') {
                    return;
                }

                var name = nodeToCopy.attr('name');
                if (!name || name.length == 0) return; // Don't bother with inputs with no names

                // Only deal with inputs we haven't seen yet
                if (!namesSeen[name]) {

                    var value;

                    if (nodeToCopy.attr('type') == 'checkbox') {
                        if (nodeToCopy.is(':checked')) {
                            value = true;
                        } else {
                            value = false;
                        }
                    } else if (nodeToCopy.attr('type') == 'radio') {
                        if (nodeToCopy.is(':checked')) {
                            value = nodeToCopy.val();
                        } else {
                            // pretend we haven't seen un-selected radio buttons so we end up with the selected one
                            return;
                        }
                    } else {
                        value = nodeToCopy.val();
                    }

                    namesSeen[name] = true;

                    addValue(obj, name, value);
                }
            });
        }); // each selector

        return obj;
    };

    var addValue = function (obj, name, value) {

        if (_options.preserveNaming) {
            obj[name] = value;
        } else {
            var parts = name.split('.');
            parts.reverse(); // must reverse to use the pop() as we will be going root to leaf.

            add(obj, parts, value); // recursevely add name parts to existing object
        }
    }

    var add = function (curObj, remainingNames, value) {

        var curName = remainingNames.pop();
        if (!curName || curName.length == 0) return; // Don't bother with blank name

        var arrayData = null;

        // see if the name is a list in the form of AAA[NNN]
        var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(curName);
        if ((match !== null) && (match.length == 3)) {
            var arrayData = { arrName: match[1], arrIndex: match[2] };

            // Make the current object and name be the array and the index of the array respectively
            // Equivalent to calling curObj[arrayData.arrName][arrIndex] in the section below
            if (curObj[arrayData.arrName] === undefined) curObj[arrayData.arrName] = [];
            curObj = curObj[arrayData.arrName];
            curName = arrayData.arrIndex;
        }

        if (remainingNames.length == 0) {
            // This is the leaf node
            // If not an array value, just place it on the leaf object.
            curObj[curName] = value;
        } else {
            // Not the leaf, have to extend existing object
            if (curObj[curName] === undefined) curObj[curName] = {};
            add(curObj[curName], remainingNames, value);
        }
    }

})(jQuery);

1st Audax of 2013

Got out on my 1st Audax of 2013. I’m aiming to do one a month (maybe 10 months, rather than 12) in order to get some sort of Audax badge.

This is the same route in reverse that I did last November and I’ve managed to knock 30 minutes off the previous run. Partly because I’ve been riding further to work and partly because the last time was in a gale (probably the more important).

I’m rapidly going off country lanes because of the huge potholes and occasional rivers across the road.

Taking the boy(s) cycling

I recently acquired a Roland Add+Bike. Unfortunately, it arrived at the same time as the snow, so it’s been sitting around for 2-3 weeks.

My older boy (just reaching 4) had gotten far to big and heavy for a child seat, also, it wasn’t an interesting experience for him. I wanted something bike like that would also allow me to carry my youngest son (almost 2). Thus a trailer was out of the reckoning, especially as our one experience with a 2 child trailer was fighting and crying (not the wife and I). Most tag-alongs didn’t allow a child seat as well, due to the mounting, the CTC also recommends strongly against ones that attach to the seat post (not sure why).

My choices were down to the Roland and the Follow Me tandem.  I plumped for the Roland in the end because it gave me a rack, which would be useful for taking my pannier bag to work and that the weight (and bouncyness) of stuff left on the bike when not carrying a child was that much less. This would be important in some sort of future scenario where I had to drop children off at school. The Follow Me would have let me “release” the boy on quiet roads, but I reckon by the time I’d let him cycle a country road, I’d let him cycle residential streets and drive / walk him over the busy bits.

Initial impressions are very good. Everything seems put together nicely and you get “proper” components: Shimano hub gear, Schwalbe tyres etc. The rack took a little while to fit and it on the heavy side (1.8kg!!!), so not one for the CF road bike, but probably a good thing when pulling your progeny.

Roland02 roland01

 

The boy seemed to really enjoy it as well and demanded “an adventure” (1 mile round the block, but don’t tell him). The day after these photos, I put the young man’s brother on the back in a seat and went for another couple of miles.

I worried about stopping without my stoker putting his feet down, but, I didn’t have any trouble balancing with one leg down and starting off was fine. I did really notice the additional drag of the tag-along, to the extent that when I added an extra 10-15kg brother in the seat, it made almost no difference. I noticed afterwards that the Roland’s tyre was on the soft side (not flat though), so pumping that up should help. Also you may notice that I still have on my super draggy ice tyres, which can’t help. Perhaps this gives insight into why the trike was so slow though…

More to come I hope. 🙂