BindingHandler in Knockoutjs. build own ListView

2019-06-13 18:35发布

I want to build my own listView with selectedItem and Itemsource.

I have started a jsFiddle http://jsfiddle.net/andersb79/53xRL/1/

Can you please help me in the right direction about how I should build this. I havn't seen a bindingHanlder that do this.

What I want is something like this.

data-bind="myListView : { items : comments, selectedItem : selectedComment}"

1条回答
一纸荒年 Trace。
2楼-- · 2019-06-13 19:07

In your custom binding you need to:

  1. Create an element for each of the items in the list
  2. Append that element to the parent
  3. Add a click handler to each element that sets the selectedItem on the original view model

I've also highlighted the current selected item for clarity.

ko.bindingHandlers.myListView = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),

            //get the list of items
            items = value.items(),
            //get a reference to the selected item observable
            selectedItem = value.selectedItem,
            //get a jQuery reference to the element
            $element = $(element),
            //get the currently selected item
            currentSelected = selectedItem();

        //clear the parent of any existing children
        $element.html("");

        for (var index = 0; index < items.length; index++) {
            (function() {
                //get the list of items
                var item = ko.utils.unwrapObservable(items[index]),

                    //create a child element with a click handler
                    $childElement = $("<li>")
                    .text(item.id() + " " + item.text())
                    .click(function() {
                        //remove selected class on all siblings
                        $(this).siblings().removeClass("selected");
                        //add selected class on this item
                        $(this).addClass("selected");
                        //set the observable 'selected item' property on the source view model
                        selectedItem(item);
                    });

                //add the selected class if this item is the current selected item
                if (item == currentSelected) {
                    $childElement.addClass("selected");
                }

                //add the child to the parent
                $element.append($childElement);
            })();
        }

    }
};

Here's a working example

Note: As I am using the update instead of the init method it will work when updating the list of comments

Update

If you want to use the contents of the original DIV as the template for each created item, you need two additional steps:

  1. Grab the original content of the element when initialized and use that in place of the hard-coded content above
  2. Apply bindings to the created element before adding it to the DOM

        ko.bindingHandlers.myListView = {
    init: function(element) {
        var $element = $(element),
            originalContent = $element.html();
    
        $element.data("original-content", originalContent);
        return { controlsDescendantBindings: true }
    },
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
    
            //get the list of items
            items = value.items(),
            //get a reference to the selected item observable
            selectedItem = value.selectedItem,
            //get a jQuery reference to the element
            $element = $(element),
            //get the currently selected item
            currentSelected = selectedItem(),
            //get the current content of the element
            elementContent = $element.data("original-content");
    
        $element.html("");
    
        for (var index = 0; index < items.length; index++) {
            (function() {
                //get the list of items
                var item = ko.utils.unwrapObservable(items[index]),
    
                    //create a child element with a click handler
                    $childElement = $(elementContent)
                    .click(function() {
                        //remove selected class on all siblings
                        $(this).siblings().removeClass("selected");
                        //add selected class on this item
                        $(this).addClass("selected");
                        //set the observable 'selected item' property on the source view model
                        selectedItem(item);
                    });
    
                ko.applyBindings(item, $childElement[0]);
    
                //add the selected class if this item is the current selected item
                if (item == currentSelected) {
                    $childElement.addClass("selected");
                }
    
                //add the child to the parent
                $element.append($childElement);
            })();
        }
    
    }
    

    };

Note that we have to prevent the content of the div being processed by knockout by returning { controlsDescendantBindings: true } from our init method.

Here is an updated example

查看更多
登录 后发表回答