Knockout.js and Select Options Binding Pre Selection

With the move into MVC4 I have taken an interest in knockout.js. Microsoft seemed fit to include it with MVC4 so it was worth taking a look at. To my surprise it will undoubtedly save me some time and effort for complex ajax UIs. I have already hit a major stumbling block though with it. That is the fact that when you bind an object to a select knockout does not work as you would expect. For example:

var Font = {FontID: 1, Alias: "Arial"}
 
var FontList = [/*An array of the structure above*/];
<select data-bind="options: $root.fontList, optionsText: 'Alias', value: Font"></select>

What you would expect is that no matter at what index the Font object was that it would select the proper item in the select menu when you loaded the page. This is not the case. Javascript does not consider two objects truly equal unless it is a reference to an exact object. This is why the menus don’t pre select the way you expect. Personally I think knockout.js needs to work the way people expect. That is, it is able to store objects as the values of a select, therefore it should have a mechanism to allow you to pre select one based on a pre determined key.

Workaround 1

Faced with this problem, one of the easiest ways around is to change the way you bind to the select. Doing below you will properly pre select the value you want. However, the only issue here is that when you update the value in the select you essentially corrupt your data. If say you have a Font object, the key would properly update, but the “Alias” value would not. Obviously this solution is not ideal.

<select data-bind="options: $root.fontList, optionsText: 'Alias', optionsValue: 'FontID', value: Font.FontID"></select>

Workaround 2

In my search for a solution many people suggested holding the selected value in a separate observable. You pre populate that observable with a reference to an object in the FontList array. This would work for a lot of people, but not for me. When I fetch my data from the server it is already nicely formatted and held in a structured object. If I have to break that structure for every drop down then the usefulness of knockout starts to come into question.

Final Solution

After toying with the idea of simply editing the knockout source code to make it work the way I expected I ended up finding out that making a custom binding handler would solve my issue. Here it is below along with the usage.

ko.bindingHandlers.preSelect = {       
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var val = ko.utils.unwrapObservable(valueAccessor());
        var newOptions = element.getElementsByTagName("option");
        var updateRequired = false;
        for (var i = 0, j = newOptions.length; i < j; i++) {
            if (ko.utils.unwrapObservable(val.value) == ko.selectExtensions.readValue(newOptions[i])[val.key]) {
                if (!newOptions[i].selected)
                {
                    ko.utils.setOptionNodeSelectionState(newOptions[i], true);//only sets the selectedindex, object still holds index 0 as selected
                    updateRequired = true;
                }
            }
        }
        if (updateRequired)
        {
            var options = allBindingsAccessor().options;
            var selected = ko.utils.arrayFirst(options, function (item) {
                return ko.utils.unwrapObservable(val.value) == item[val.key];
            });
            if (ko.isObservable(bindingContext.$data[val.propertyName])) {
                bindingContext.$data[val.propertyName](selected); // here we write the correct object back into the $data
            } else {
                bindingContext.$data[val.propertyName] = selected; // here we write the correct object back into the $data
            }
        }
    }
};
<select data-bind="options: $root.fontList, optionsText: 'Alias', value: Font, preSelect: {key : 'FontID', propertyName : 'Font', value : Font.FontID}"></select>

In my opinion this is not an idea solution because of all the data I had to pass back in the preSelect argument. In my particular situation because of the way my objects are structured I had to know all three parameters. You situation may be different so adjust the code accordingly. I found that even though you set the selected index using knockouts method it does not update the referencing object, so the last line is there to do that. Overall this solution solves my issue and I haven’t found any pitfalls yet, but if you do let me know!

Tagged: ,

Preload images in IE6

I spent a significant chunk of time today diagnosing an image preload bug with IE6.  Bug probably isn’t a fair word, but for a browser that is one big bug why not.   In short preloading worked as it should in every other browser I test against except for IE6.  IE6 would preload the image, onload event fires, I set the src for the image and it continued to download the image again! 

Doesn’t work

pic1= new Image();
pic1.src='/image.gif';
pic1.onload = function(){docimage.src='/images.gif'};

Works

pic1= new Image();
pic1.src='http://site.com/image.gif';
pic1.onload = function(){docimage.src='http://site.com/image.gif'};

*sigh*, who would’ve thought.  You MUST have the full path in the  src link or IE6 won’t preload it.  It really crossed my mind today to just bite the bullet and not support IE6 at all.  It still appears that about 25% of our visitors are IE6.  Don’t even get me started on the hoops I have to jump through because of no png support.

Tagged: , ,

Debug your javascript in style

So basically today I wanted to plug a plug-in (no pun intended) that I have really found to be invaluable to debug javascript.  The plug-in is firebug for FireFox.  In my recent dive into Dojo words cannot describe how convenient and handy firebug is.  The debug console alone is enough to make it worth while because Microsoft just doesn’t have anything like it at the moment. No need wasting my breathe explaining what all it does, click the link and see for yourself.  If you develop in javascript at all you’re missing out big time not using firebug.

Tagged: , ,

Javascript 2.0

Jeremy Martin posted up a nice run down of the proposed Javascript 2.0 spec:

Well I suppose it’s an undeniable fact about us programmer-types – every now and then we just can’t help but get excited about something really nerdy. For me right now, that is definitely JavaScript 2.0. I was just taking a look at the proposed specifications and I am really, truly excited about what we have coming.

This is a pretty big deal all in all.  Web 2.0 and AJAX type sites are all driven with javascript.  Looking over some of the proposed additions it should be something to look forward to!

Tagged: , , ,