The kncokout js library is amazing. It's capable of making even the most complex JavaScript applications super simple and robust. Obviously it's not ideal in al situations, and there are a bunch of libraries that do similar things, but this one is incredible and it scales so well.

Enter Knockout!

We're now making a fairly large ASP.NET MVC application and we need a nice way of displaying and saving arrays of objects in our forms. Obviously there is no standard MVC EditorFor for doing this, so we looked at ways to make our own.

"Wouldn't it be cool if we didn't have to worry about arrays of JSON objects, having to send them to the page, edit them and then post them back?" Said one developer.
"That would be super cool... but how?" Said another.

Enter knockout! 

We can do this with a knockout binding. And we don't have to worry about setting model data to JavaScript objects, editing them or posting them back. That can all be handled. Here's how:

First we have an HTML element that uses the binding

<div data-bind="hiddens: { data: @Json.Encode(model.ComplicatedArray), name: 'ComplicatedArray' }">

Are you with me so far? Then we define our binding that looks a little something like this:

ko.bindingHandlers.hiddens = { 
    update: function(element, valueAccessor) { 
        var value = ko.mapping.toJS(valueAccessor().data); 
        $(element).empty().append(objectToHiddenDom(value, valueAccessor().name)); 
    } 
};

function objectToHiddenDom(value, namePrefix, hiddenArray) { 
    if (!hiddenArray) { 
        hiddenArray = []; 
    } 
    
    if (utils.isArray(value)) { 
        for (var i = 0; i < value.length; i++) { 
            utils.objectToHiddenDom(value[i], namePrefix + '[' + i + ']', hiddenArray); 
        } 
    } else if (utils.isObject(value)) { 
        for (var key in value) { 
            utils.objectToHiddenDom(value[key], namePrefix + '.' + key, hiddenArray); 
        } 
    } else { 
        // simple type, push to array 
        hiddenArray.push($('<input />').attr('type', 'hidden').attr('name', namePrefix).val(value)); 
    }
    return hiddenArray; 
};

When you run it your DOM might end up with a little something like this:

<div data-bind="hiddens: { data: [], name: 'ComplicatedArray' }">
    <input type="hidden" name="ComplicatedArray[0].Id" value="_1367629733949">
    <input type="hidden" name="ComplicatedArray[0].Name" value="Thing 1">
    <input type="hidden" name="ComplicatedArray[1].Id" value="_1367629738172">
    <input type="hidden" name="ComplicatedArray[1].Name" value="Thing 2">
    <input type="hidden" name="ComplicatedArray[2].Id" value="_1367629742061">
    <input type="hidden" name="ComplicatedArray[2].Name" value="Another Thing">
</div>

You don't have to care about what's inside the object, it doesn't even have to be an array. It figures all that out. This code does, however, have some performance issues with very very very large objects because it empties the DOM and recreates it every time. If anyone has any suggestions about this I would love to hear them.

Cheers, Rich
By Richard Rout
Feed
0 Comments
Only used for Gravatar avatar.