1

I am trying to have the functionality of the jQuery selectable list when using KnockoutJS, but can't get it to work.

Using Custom Bindings I was successful in transforming ul element in a selectable one and creating events handlers for the selected and unselected events. But what do I pass to my ViewModel functions which maintain the selected elements?

Here is my code so far: http://jsfiddle.net/QCmJt/

I am new to all this, so maybe I am approaching this the wrong way...

Community
  • 1
  • 1
joerage
  • 4,863
  • 4
  • 38
  • 48

2 Answers2

1

Here is a little bit of a modified one from yours, got it working as well. It uses a selected property on each item in the array that's an observable to do two way communication. If you want a property that's just a list of the selected items, you could always create a dependent observable, or just a method to filter.

http://jsfiddle.net/QCmJt/32/

Created a custom binding:

ko.bindingHandlers.selectableItem = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var selectable = $(element).parent();

        selectable.bind('selectableselected', function(event, ui) {
            if(ui.selected === element) {                
                var value = valueAccessor();

                value(true);
            }
        });

        selectable.bind('selectableunselected', function(event, ui) {
            if(ui.unselected === element) {                
                var value = valueAccessor();

                value(false);                
            }
        });
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        var el = $(element);

        if(valueAccessor()()) {
            el.addClass('ui-selected');
        } else {
            el.removeClass('ui-selected');
        }
    }
};

It's not hardened for more than very simple two way bindings, but should be easy enough to build on.

Paul Tyng
  • 7,924
  • 1
  • 33
  • 57
  • Thanks Paul! That is a nice way of approaching this. My only concern is with mixing UI row selection status within the view model property 'requests'. In my scenario the 'requests' property is synched back to the back end and so I rather use another property to hold the status of the selected row. – joerage Nov 03 '11 at 01:05
  • @joerage Yeah I know what you mean. I saw thats how you had it represented and tried to get it to work that way, but no real luck. You have to look at this as MVVM, since thats Knockout's purpose. In MVVM a ViewModel is an abstraction of the view and much more tightly bound to the UI, not necessarily your domain objects, so it makes sense that if these UI objects have a property indicating selection status that its reflected in the ViewModel. If you need that representation different for persistence, then you can always remap the objects on the server. – Paul Tyng Nov 03 '11 at 11:10
0

Ok, I figured a way to pass the data bound to the selected item:

$(ui.selected).tmplItem().data when selecting and $(ui.selected).tmplItem().data when unselecting.

Here the updated fiddle: http://jsfiddle.net/8RnxC/

Custom binding looks like:

ko.bindingHandlers.selectable = {
        init: function(element, valueAccessor) {
            $(element).attr("id", "selectable").selectable({
                selected: function(event, ui) {
                    vm.selectRequest($(ui.selected).tmplItem().data);
                },
                unselected: function(event, ui) {
                    vm.unselectRequest($(ui.unselected).tmplItem().data);
                }
            });
        }
    };

And my view model:

function viewModel() {
        // Data
        this.requests = ko.observableArray([]);
        this.selectedRequests = ko.observableArray([]);

        // Behaviours
        this.selectRequest = function(request) {
            this.name = "othername";
            // Add to array if not already there.
            if ($.inArray(request, this.selectedRequests()) === -1) {
                this.selectedRequests().push(request);
            }
            var self = this;

        };
        this.unselectRequest = function(request) {
            // Remove from the array.
            this.selectedRequests().splice($.inArray(request, this.selectedRequests()), 1);
        };
    };
joerage
  • 4,863
  • 4
  • 38
  • 48