Multiselect is not compatible with KO …. Pish posh! We’ll make it fit!

When looking for a KO compatible alternative way to display data and be able to move between 2 lists I found a delightful widget that had been created by QuasiPartikel – they had a list selection widget with search capability and it was all driven from a single select list that had multiple enabled.

Unfortunately – the way that it had been developed was that it iterated through the list of items that were in the select list, and created a whole new  set of html to represent the data that we were using. For static data and only having to use it for a bit of a muck around it was already great, however, when using knockout JS in conjunction with it, it severely lacked in functionality that was fundamental to the way that knockout was designed.

Unfortunately I rather naively assumed that this wouldn’t be a problem – and only realised this half way through implementation and after adding a couple of bits of additional functionality that were required for the product to be used.

So, there I was, happily including this flashy new functionality in to the front end of our sparkling new system that we were building, when all of a sudden a fundamental blocker appears in front of me; putting the brakes on the associated functionality massively … we couldn’t save the result. Oops.

This was not the time to be dropping functionality, as we had a client demo coming up – so we put the functionality on hold, reinstated the old hard coded representation of the functionality we were trying to obtain and went quietly on with adding more valuable and time pressing bits of wonderfulness!

2 Months Later …

A fair bit of time has passed and as with everything, it’s been out of site, out of mind. So when I finally got to the point where we could actually spend some time on this (… and there was nothing else to do) I finally had to face up to this and make a decent go of either getting this functionality working, finding a different plugin that was compatible with KO or look at different ways to do this all together.

After an exhausting search on the web it appeared that no one seems to do any widgets that claim to be knockout compatible, or designed to be used by Knockout. Is this possible? As it appears that Knockout is growing massively in web development, they provide some of the best documentation and tutorials on the web (IMO) and yet there aren’t any decent tools that are used with it to the extent that you can guarantee it works and persists the model.

I will note that if anyone can send links to something that is designed to be used by knockout then please feel free! andy.brown@comade.co.uk.

So that rules out finding something else, the client was fairly set on the way in which they wanted us to present and manipulate the information and I quite fancied learning something about fannying around with jQuery widgets … which leads nicely into me amending the original code that was provided by Quasipartikel to suit our needs and make it KO compatible.

Implementation

So the theory that Quasipartikel had adopted was to simply create a representation of the select box in new html and the select box gets updated in the back ground as a part of the process of moving the items from list to list.

Having already added a couple of bits of functionality, I had the basic gist of writing code in widget format and not wanting to invest enough time to completely rewrite the widget; I thought I would stick with the convention and continue to manipulate the collection as I add, remove etc so that the collection would remain up to date at any point in time.

So I added a couple of internal methods to be able to handle the KO collection that I was using to select items within the multiselect. These were to handle adding and removing items from the collection as I added and removed them from the UI side of things.

This was using a simple bit of adding and removing items from the array that was being held for the select list source. This was using a hardcoded reference to the collection that the Select was driven by, I thought that I could just worry about whether the idea was feasible first and then worry about getting a reference passed in at some point in the future.

So, with adding and removing the items from the relevant collection I could try and link this in to the UI functionality – but I had a brain wave at this point, rather than linking it to the UI side of things I simply called the add and remove from the part that added and removed from the original functionality – that way it would be exactly the same as the UI all the time.

Referencing the collection was the next part – I had it working to the point where I was satisfied that the functionality would handle what we wanted from it I could invest some proper time in refining it.

I solved the reference by actually getting the KO data-bind from the object. This then allowed me to manipulate the string that was being passed in and return whatever item was being put in as the SelectedOptions array.

The final hurdle that I had to leap was based around loading times. I’ve always had an issue with this, ever since we introduced the KO mapping to auto create our ViewModels. The benefits to us are huge for using mapping, so getting rid was not an option.

The history behind the bug was that when we load the item, we don’t know which of the sub items we will be presenting in the multiselect until it is selected on the UI. This means that when we map the object it is empty and therefore KO thinks that it doesn’t have the right stuff to populate the multiselect and therefore sacks it off and says ‘I’ve had enough, I can’t do anything else’ and so the rest of the items on the page do no load correctly. ANNOYING!

Anyway, getting round this was a bit of UI fun rather than editing the widget. As the sub items appear in the UI as a popup, I tagged on to the button that instantiates that popup the method that populates the viewModel with the selected item. This allowed the items to load correctly if they were loaded first. It unfortunately did not work when refreshing the multiselect.

This is where the ‘refresh’ public method comes in for the widget. Simply implementing that and calling it if it is not the first item to instantiate it (or just leaving it to build up the item and then refreshing it straight away) works a treat.

So, the only thing that remains is to present the code and the way to use it.

I have implement it by calling the JS $(‘#id-of-multiselect .multiselect’).multiselect(); to start the multselect item. Then if need be I call $(‘#id-of-multiselect .multiselect’).multiselect(‘refresh’); to ensure that the viewModel is up to date with the multiselect item.

In the HTML I create the item with

<select multiple class="multiselect" 
 data-bind="options: viewModel.UnselectedElements(), 
 optionsValue: function(item){return item.Id}, 
 optionsText: function(item){return item.Value}, 
 selectedOptions: viewModel.IncludedElementIds()"> </select>.

The UnselectedElements are a keyvaluepair of string and integer, the IncludedElementIds is a list of integers.

Here is a link that includes the code that I have used. I will also take this opportunity to include another mention that this code is based on QuasiPartikel’s original code which can be found here.

Javascript

Apologies for not posting the JS code, it wouldn’t let me upload a JS file as a download (Damn you WORDPRESSSS!!!) – so instead I have created a fiddle to hold the JS here