1

I'm pretty new to Knockout.js and am facing now a situation where I'm not really sure how to handle it right. The thing is: I got a bunch of objects that I'm retrieving via ajax. The result will be somewhat like this:

var Objects = [ { id: 0, name: "Foo", type: "A" },
                { id: 1, name: "Bar", type: "B" },
                { id: 1, name: "Bar", type: "A" }, ... ];

What I've done so far (simplified):

var ViewModel = function() {
    var self = this;
    self.objects = ko.observableArray(Objects);
};

Now I need to render those objects in different lists depending on their "type". So there is a list of objects of type "A" and one for the objects of type "B" etc. (there are five types at the moment, but there may be added some more in the future).

I've come up with this (working) solution:

var ViewModel = function() {
    var self = this;
    self.objects = ko.observableArray(Objects);

    self.objectsA = ko.computed(function() {
        return ko.utils.arrayFilter(self.objects(), function(item) {
            return (item.type == 'A');
        });
    });

    self.objectsB = ...
    self.objectsC = ...
};

And in my actual view:

<h1>Type A</h1>
<ul class="typeA" data-bind="template: { name: 'object', foreach: objectsA }"></ul>

<h1>Type B</h1>
<ul class="typeB" data-bind="template: { name: 'object', foreach: objectsB }"></ul>

Is there a better way to address this problem? This works, but it is a bit ugly, not really dynamic and includes a lot of repetition.

2 Answers 2

5

Bindings are executed inside of a computed observable. This means that you can choose to use a simple function that takes a parameter rather than an actual computed observable if you would like.

This means that you can simplify it down to:

var ViewModel = function() {
    var self = this;
    self.objects = ko.observableArray(Objects);

    self.filterByType = function(type) {
        return ko.utils.arrayFilter(self.objects(), function(item) {
            return (item.type === type);
        });
    };
};

Then, bind against it like:

<h1>Type A</h1>
<ul class="typeA" data-bind="template: { name: 'object', foreach: filterByType('A') }"></ul>

<h1>Type B</h1>
<ul class="typeB" data-bind="template: { name: 'object', foreach: filterByType('B') }"></ul>

Your UI will update now whenever the array is manipulated (items added/removed). However, if you are going to be editing the type on the fly, then type would need to be an observable for the computed observable to update (in your original method or in this way).

Sample here: http://jsfiddle.net/rniemeyer/NFbxc/

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that made it all a lot clearer. I'm gonna go with this!
1

You could just let Knockout do the filtering like this:

<h1>Type A</h1>
<ul class="typeA" data-bind="template: { name: 'object', foreach: objects }">
    <li data-bind="if: type = 'A'"><!--whatever mark-up you wanted here--></li>
</ul>

And then do the same for the other types. Or, you could even have you ViewModel hold an array of types and do the whole thing with one template.

Edit: Including RP Niemeyer's answer, you could do this:

var ViewModel = function() {
    var self = this;
    self.objects = ko.observableArray(Objects);
    self.types = ["A","B","C","D"];

    self.filterByType = function(type) {
        return ko.utils.arrayFilter(self.objects(), function(item) {
        return (item.type === type);
        });
    };
};

<!-- ko foreach types -->
    <h1> Type <span data-bind="text: $data"></h1>
    <ul data-bind="{attr: {class = 'type' + $data}, template: { name: 'object', foreach: filterByType($data)}}"></ul>
<!-- /ko -->

Now adding a type E is a simple as adding "E" to you array of types.

1 Comment

You mean, like two nested foreach loops? Is that possible?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.