Search code examples
c#knockout.jsknockout-2.0

GroupBy, Filtering in knockout.js


How can i groupby, and filter in a table using knockout.js. I also try to expand and collapse row and this the code that I am somewhat successfull).

This is my distinc function, note at the moment it seem to only work on one data element ( i would like it to have it in the future to be distinct on a property for an object.

ko.observableArray.fn.distinct = function (prop) {
    var target = this;
    target.index = {};
    target.index[prop] = ko.observable({});

    ko.computed(function () {
        //rebuild index
        var propIndex = {};

        ko.utils.arrayForEach(target(), function (item) {
            var key = ko.utils.unwrapObservable(item[prop]);
            if (key) {
                propIndex[key] = propIndex[key] || [];
                propIndex[key].push(item);
            }
        });

        target.index[prop](propIndex);
    });

    return target;
};  

This is my current object function

course(_id, _courseCode, _courseTitle, _courseCampus) {
    var self = this;
    this.id = ko.observable(_id);
    this.courseCode = ko.observable(_courseCode);
    this.courseTitle = ko.observable(_courseTitle);
    this.coursecampus = ko.observable(_courseCampus);
}

There is my viewmodel

function ViewModel() {
    var self = this;
    this.courses = ko.observableArray().distinct('courseCode');
    this.gpCourseCode = ko.observableArray();
    this.mutated = ko.observableArray();

    this.switchMutated = function (code) {
        if (self.mutated.indexOf(code) > -1) {
            self.mutated.push(code);
        }
        else {
            self.mutated.remove(code);
        }
    };

    this.switchText = function (code) {
        if (self.mutated.indexOf(code) > -1) {
            return "-"
        }
        else {
            return "+"
        }
    };

    self.gpCourseCode.push("MATH1030");
    self.gpCourseCode.push("MATH1006");
    self.gpCourseCode.push("MATH1046");
    self.courses.push(new course("1", "MATH1030", "Calculus", "city1"));
    self.courses.push(new course("2", "MATH1030", "Calculus", "city2"));
    self.courses.push(new course("3", "MATH1030", "Calculus", "city3"));
    self.courses.push(new course("4", "MATH1006", "Linear algebra", "city1"));
    self.courses.push(new course("5", "MATH1046", "Discrete Math", "city2"));
    self.courses.push(new course("6", "MATH1006", "Linear algebra", "city2"));
    self.courses.push(new course("7", "MATH1046", "Discrete Math", "city1"));

    this.searchCode = ko.observable("");
    self.filteredRecords = ko.computed(function () {
        return ko.utils.arrayFilter(self.gpCourseCode(), function (rec) {
            return (
                (self.searchCode().length == 0 || rec.toLowerCase().indexOf(self.searchCode().toLowerCase()) >= 0)
            )
        });
    });
}

This is my html

<table class="table">
    <thead>
        <tr>
            <th>ID</th>
            <th>Course Code</th>
            <th>Course Title</th>
            <th>Course Campus</th>
        </tr>
        <tr>
            <th><input data-bind="value: searchCode" placeholder="Course Code" /></th>
            <th></th>
            <th></th>
            <th></th>
        </tr>

    </thead>
    <tbody data-bind="foreach: filteredRecords">
        <tr class="table-dark">
            <td><span data-bind="text: $data"></span></td>
            <td><span></span></td>
            <td></td>
            <td class="text-right">
                <button class="btn btn-secondary" data-bind="click: $root.switchMutated($data), text: $root.switchText($data)"></button>
            </td>
        </tr>
        <!-- ko foreach: $root.courses.index.courseCode()[$data] -->
        <tr data-bind="css: { mutated: $root.mutated.indexOf($data.courseCode()) > -1 }">
            <td><span data-bind="text: $data.id"></span></td>
            <td><span data-bind="text: $data.courseCode"></span></td>
            <td><span data-bind="text: $data.courseTitle"></span></td>
            <td><span data-bind="text: $data.coursecampus"></span></td>
        </tr>
        <!-- /ko -->
    </tbody>
</table>

Here are my two issues

  1. When I filter the search, my mutable array becomes empty, therefore all the element become expanded.
  2. I would love to convert my gpCourseCode to be a class similar to course, but I am not to sure how to do a distinct on a class and not just one element.

I have a jsfiddle here


Solution

  • When I filter the search, my mutable array becomes empty, therefore all the element become expanded.

    That's because you have an error in your click handler. It should take a function reference and not a function call. So click: $root.switchMutated($data) should become: click: $root.switchMutated (within a foreach, the click handler gets passed the current item as its first parameter automatically).

    You will however now have to initialize the mutated array yourself (or maybe reverse the logic so you don't have to). Your click handler was doing this by accident, since supplying a function call to the click handler will cause that function to be executed.