Search code examples
knockout.jsko.observablearray

Knockout Filtered Binding


I'm looking for some guidance on how to get the following to work: http://jsfiddle.net/gdefilippi/qgr1s51s/4/

In the fiddle above I've got two observableArrays. One provides a filter to the other. This will be applied to async calls in a larger project.

Anyway I'm looping on the segments, then looping on the items filtered by segment. Everything is good there.

When I push a new item into the items observableArray the result is not rebound. How would I get the rebind to work correctly and easily. In the below code and in the Fiddle looking to have the add which pushes an item into the observableArray items (see the add function) update, the issue is the binding is against the filtering function.

Samples:

var vm = function(){
    var self = this;
    self.items = ko.observableArray([
        {label:'item1',segment:1},
        {label:'item2',segment:2}
    ]);
    self.segments = ko.observableArray([
        {title:'Segment 1',id:1},
        {title:'Segment 2',id:2}
    ]);
    self.bysegment = function(segment){
        return ko.utils.arrayFilter(self.items(), function (_item){
            return _item.segment === segment;
        });
    };
    self.add = function(id){
        self.items.push({
            label:'new',
            segment:id
        });
        alert(items().length)
    };
};
ko.applyBindings(vm);

<!-- ko foreach: segments -->
<h1 data-bind="text: title + ' : ' + id"></h1>
    <!-- ko foreach: bysegment(id) -->
    <span data-bind="text: label + ' : ' + segment"></span>
    <!-- /ko -->
<button type="button" data-bind="click: add">Add</button>
<!-- /ko -->

Thanks, Geoffrey


Solution

  • Your by segment function is not a knockout observable variable so it cannot update the ui. You can make it a computed variable and when the observable variables you use to get the computed variable is updated, the computed variable will be updated too. So that is your list.

    or You can build a more structured model binding by getting the segments to a seperate model like ;

    Here i changed your fiddle

    var segment = function (data) {
        var self = this;
    
        self.id = ko.observable(data.id);
        self.title = ko.observable(data.title);
    
        self.items = ko.observableArray(data.items);
    
    
        self.newItem = function (data) {
            self.items.push({
                label: 'new',
                segment: data.id()
            });
            alert(self.items().length);
        }
    }
    

    and in your main vm

    var vm = function () {
        var self = this;
        var dbItems = [{
            label: 'item1',
            segment: 1
        }, {
            label: 'item2',
            segment: 2
        }];
    
        var dbSegments = [{
            title: 'Segment 1',
            id: 1
        }, {
            title: 'Segment 2',
            id: 2
        }];
    
        self.segments = ko.observableArray([]);
    
        ko.utils.arrayForEach(dbSegments, function (item) {
            self.segments.push(new segment({
                id: item.id,
                title: item.title,
                items: ko.utils.arrayFilter(dbItems, function (_item) {
                    return _item.segment === item.id;
                })
            }));
        });
    
    };
    ko.applyBindings(vm);