javascriptknockout.jsknockout-2.0

How to nest object in Knockout.js


In Knockout.js, what is the best way to nest object inside of an an array?

I am trying to have an observable that is sorted by a group of courses title and inside there would be different course offered to a student with the same group property ( course code).

This is my snippet

function course(_id, _code, _title, _campus) {
    var self = this;
    this.id = ko.observable(_id);
    this.courseCode = ko.observable(_code);
    this.courseTitle = ko.observable(_title);
    this.coursecampus = ko.observable(_campus);
}

function gpCourseProperties(_code, _isHidden) {
    var self = this;
    this.gpCode = ko.observable(_code);
    this.hide = ko.observable(_isHidden);
    this.courses = ko.observableArray();

    this.addCourse = function (_id, _courseCode, _courseTitle, _courseCampus) {
        self.courses.push(new course(_id, _courseCode, _courseTitle, _courseCampus));
    }

    this.switchMutated = function (code) {
        self.hide(!self.hide());
    };
    this.switchText = ko.computed(function () {
        if (self.hide() == true) {
            return '+'
        }
        return '-';
    }, this);
}

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

self.gpCourseProp.push(new gpCourseProperties("MATH1030", true));
self.gpCourseProp.push(new gpCourseProperties("MATH1006", true));
self.gpCourseProp.push(new gpCourseProperties("MATH1046", true));

    for (i = 0; i < self.gpCourseProp().length; i++) {
    if (self.gpCourseProp()[i].gpCode == "MATH1030") {
        self.gpCourseProp()[i].addCourse(new course("1", "MATH1030", "Calculus", "City1"));
        self.gpCourseProp()[i].addCourse(new course("2", "MATH1030", "Calculus", "City2"));
        self.gpCourseProp()[i].addCourse(new course("3", "MATH1030", "Calculus", "City3"));
    }
    if (self.gpCourseProp()[i].gpCode == "MATH1006") {
        self.gpCourseProp()[i].addCourse(new course("4", "MATH1006", "Linear algebra", "City1"));
        self.gpCourseProp()[i].addCourse(new course("6", "MATH1006", "Linear algebra", "City2"));
    }
    if (self.gpCourseProp()[i].gpCode == "MATH1046") {
        self.gpCourseProp()[i].addCourse(new course("5", "MATH1046", "Discrete Math", "City1"));
        self.gpCourseProp()[i].addCourse(new course("7", "MATH1046", "Discrete Math", "City2"));
    }
    }
}

var vm = new ViewModel();
ko.applyBindings(vm);
    tr.mutated {
        display: none;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table class="table">
    <thead>
        <tr>
            <th>ID</th>
            <th>Course Code</th>
            <th>Course Title</th>
            <th>Course Campus</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: gpCourseProp">
        <tr class="table-dark">
            <td></td>
            <td><span data-bind="text: $data.gpCode"></span></td>
            <td><span></span></td>
            <td></td>
        </tr>
        <!-- ko foreach: $data.courses -->
        <tr data-bind="css: { mutated: $parent.mutated.hide() == true }">
            <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>

The issue i have is trying to add courses to the gpCourseProperties.


Solution

  • Main issues are:

    Here's an updated version with the minimum changes required to make it work. (note, I removed the CSS that hid some of the rows for debug purposes).

    The fixes are marked in the comments.

    function course(_id, _code, _title, _campus) {
        var self = this;
        this.id = ko.observable(_id);
        this.courseCode = ko.observable(_code);
        this.courseTitle = ko.observable(_title);
        this.coursecampus = ko.observable(_campus);
    }
    
    function gpCourseProperties(_code, _isHidden) {
        var self = this;
        this.gpCode = ko.observable(_code);
        this.hide = ko.observable(_isHidden);
        this.courses = ko.observableArray();
    
        this.addCourse = function (_id, _courseCode, _courseTitle, _courseCampus) {
            self.courses.push(new course(_id, _courseCode, _courseTitle, _courseCampus));
        }
    
        this.switchMutated = function (code) {
            self.hide(!self.hide());
        };
        this.switchText = ko.computed(function () {
            if (self.hide() == true) {
                return '+'
            }
            return '-';
        }, this);
    }
    
    function ViewModel() {
        var self = this;
        this.gpCourseProp = ko.observableArray();
    
        // Fix 1:
        //                     vvvvvvvvvvvvvvvvvvvvvvv                v
        self.gpCourseProp.push(new gpCourseProperties("MATH1030", true));
        self.gpCourseProp.push(new gpCourseProperties("MATH1006", true));
        self.gpCourseProp.push(new gpCourseProperties("MATH1046", true));
    
        for (i = 0; i < self.gpCourseProp().length; i++) {
            // Fix 2:
            //                               vv
            if (self.gpCourseProp()[i].gpCode() == "MATH1030") {
                self.gpCourseProp()[i].addCourse("1", "MATH1030", "Calculus", "City1");
                self.gpCourseProp()[i].addCourse("2", "MATH1030", "Calculus", "City2");
                self.gpCourseProp()[i].addCourse("3", "MATH1030", "Calculus", "City3");
            }
            if (self.gpCourseProp()[i].gpCode() == "MATH1006") {
                self.gpCourseProp()[i].addCourse("4", "MATH1006", "Linear algebra", "City1");
                self.gpCourseProp()[i].addCourse("6", "MATH1006", "Linear algebra", "City2");
            }
            if (self.gpCourseProp()[i].gpCode() == "MATH1046") {
                self.gpCourseProp()[i].addCourse("5", "MATH1046", "Discrete Math", "City1");
                self.gpCourseProp()[i].addCourse("7", "MATH1046", "Discrete Math", "City2");
            }
        }
    }
    
    var vm = new ViewModel();
    ko.applyBindings(vm);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Course Code</th>
                <th>Course Title</th>
                <th>Course Campus</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: gpCourseProp">
            <tr class="table-dark">
                <td></td>
                <td><span data-bind="text: gpCode"></span></td>
                <td><span></span></td>
                <td></td>
            </tr>
            <!-- ko foreach: courses -->
            <tr data-bind="css: { mutated: $parent.hide() == true }">
                <td><span data-bind="text: id"></span></td>
                <td><span data-bind="text: courseCode"></span></td>
                <td><span data-bind="text: courseTitle"></span></td>
                <td><span data-bind="text: coursecampus"></span></td>
            </tr>
            <!-- /ko -->
        </tbody>
    </table>